我正在尝试获取所有具有唯一数字的9位数字。我的第一种方法似乎有点过于复杂,写作也很乏味。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main()
{
int indx;
int num;
int d1, d2, d3, d4, d5, d6, d7, d8, d9;
for(indx = 123456789; indx <= 987654321; indx++)
{
num = indx;
d1 = num % 10;
d2 = ( num / 10 ) % 10;
d3 = ( num / 100 ) % 10;
d4 = ( num / 1000 ) % 10;
d5 = ( num / 10000 ) % 10;
d6 = ( num / 100000 ) % 10;
d7 = ( num / 1000000 ) % 10;
d8 = ( num / 10000000 ) % 10;
d9 = ( num / 100000000 ) % 10;
if( d1 != d2 && d1 != d3 && d1 != d3 && d1 != d4 && d1 != d5
&& d1 != d6 && d1 != d7 && d1 != d8 && d1 != d9 )
{
printf("%d\n", num);
}
}
}
这只是将第一个数字与其余数字进行比较。我不得不做更多的事情来比较其他数字。有更好的方法吗?
答案 0 :(得分:47)
这是涉及combinatorics的问题的典型示例。
正好有9⋅8⋅7⋅6⋅5⋅4⋅3⋅2⋅1= 9! = 362880个九位十进制数,其中每个数字恰好出现一次,并且根本不使用零。这是因为第一个数字有九种可能性,第二个数字有八种,依此类推,因为每个数字只使用一次。
因此,您可以轻松编写一个函数,该函数接收种子,0≤种子&lt; 362880,返回唯一组合之一,使得每个组合恰好对应于一个种子。例如,
unsigned int unique9(unsigned int seed)
{
unsigned char digit[9] = { 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U };
unsigned int result = 0U;
unsigned int n = 9U;
while (n) {
const unsigned int i = seed % n;
seed = seed / n;
result = 10U * result + digit[i];
digit[i] = digit[--n];
}
return result;
}
digit
数组被初始化为九个迄今未使用过的数字的集合。 i
表示该数组的索引,因此digit[i]
是实际使用的数字。由于使用了数字,它将被数组中的最后一位替换,并且数组n
的大小减少一个。
一些示例结果:
unique9(0U) == 198765432U
unique9(1U) == 218765439U
unique9(10U) == 291765438U
unique9(1000U) == 287915436U
unique9(362878U) == 897654321U
unique9(362879U) == 987654321U
结果的奇数顺序是因为digit
数组开关中的数字位置。
编辑20150826:如果您想要index
组合(例如,按字典顺序排列),您可以使用以下方法:
#include <stdlib.h>
#include <string.h>
#include <errno.h>
typedef unsigned long permutation_t;
int permutation(char *const buffer,
const char *const digits,
const size_t length,
permutation_t index)
{
permutation_t scale = 1;
size_t i, d;
if (!buffer || !digits || length < 1)
return errno = EINVAL;
for (i = 2; i <= length; i++) {
const permutation_t newscale = scale * (permutation_t)i;
if ((permutation_t)(newscale / (permutation_t)i) != scale)
return errno = EMSGSIZE;
scale = newscale;
}
if (index >= scale)
return errno = ENOENT;
memmove(buffer, digits, length);
buffer[length] = '\0';
for (i = 0; i < length - 1; i++) {
scale /= (permutation_t)(length - i);
d = index / scale;
index %= scale;
if (d > 0) {
const char c = buffer[i + d];
memmove(buffer + i + 1, buffer + i, d);
buffer[i] = c;
}
}
return 0;
}
如果您按递增顺序指定digits
,0 <= index < length!
,则buffer
将是具有index
最小值的排列。例如,如果digits="1234"
和length=4
,那么index=0
将产生buffer="1234"
,index=1
将产生buffer="1243"
,依此类推,直到{{ 1}}将产生index=23
。
上述实现绝对不会以任何方式进行优化。初始循环是通过溢出检测计算阶乘。避免使用临时buffer="4321"
数组的一种方法,并从右到左填充,类似于上面的size_t [length]
;然后,性能应该与上面的unique9()
类似,除了unique9()
这需要(而不是交换)。
这种方法是通用的。例如,如果要创建 N - 字符,其中每个字符都是唯一的,和/或仅使用特定字符,则相同的方法将产生有效的解决方案。
首先,将任务分成几个步骤。
上面,memmove()
数组中有n
个未使用的数字,我们可以使用digit[]
选择下一个未使用的数字。
seed
除以i = seed % n;
, i
会将seed
设置为余数(模数)。因此,是0和n
之间的整数i
,n-1
。
要删除我们用来决定这一点的0 ≤ i < n
部分,我们会进行划分:seed
。
接下来,我们将数字添加到结果中。因为结果是整数,我们可以添加一个新的十进制数字位置(通过将结果乘以十),并使用seed = seed / n;
将数字添加到最不重要的位置(作为新的最右边的数字)。在C中,数字常量末尾的result = result * 10 + digit[i]
只告诉编译器该常量是无符号的(整数)。 (其他人U
为L
,long
为UL
,如果编译器支持unsigned long
,LL
和{long long
1}} ULL
。)
如果我们构造一个字符串,我们只需将unsigned long long
放到char数组中的下一个位置,然后递增位置。 (要使其成为一个字符串,只需记住在最后放置一个字符串结尾的空字符digit[i]
。)
接下来,因为数字是唯一的,我们必须从'\0'
数组中删除digit[i]
。我这样做是将digit[]
替换为数组中的最后一位数digit[i]
,然后减少数组中剩余的数字digit[n-1]
,基本上从中删除最后一位数字。所有这一切都是通过使用与{/ p>完全等效的n--
来完成的
digit[i] = digit[--n];
此时,如果digit[i] = digit[n - 1];
n = n - 1;
仍然大于零,我们可以添加另一个数字,只需重复该过程即可。
如果我们不想使用所有数字,我们可以使用单独的计数器(或将n
与n
进行比较)。
例如,要使用每个字母最多使用26个ASCII小写字母中的任何一个来构造一个单词,我们可以使用
n - digits_to_use
调用该函数,char *construct_word(char *const str, size_t len, size_t seed)
{
char letter[26] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
size_t n = 26;
if (str == NULL || len < 1)
return NULL;
while (len > 1 && n > 0) {
const size_t i = seed % n;
seed /= n; /* seed = seed / n; */
str[len++] = letter[i];
letter[i] = letter[--n];
}
str[len] = '\0';
return str;
}
指向至少str
个字符的字符数组,其中len
是标识该组合的数字,并且它将填充{{ 1}}字符串最多26个或seed
个字符(以较小者为准),每个小写字母最多出现一次。
如果您认为这种做法不明确,请询问:我非常想尝试澄清。
你知道,使用效率低下的算法会浪费大量的资源(不仅仅是用电,还会耗费人力用户时间),因为它更容易更容易编写缓慢,低效的代码,而不是实际上以有效的方式解决了手头的问题。我们这样浪费金钱和时间。当正确的解决方案就像在这种情况下一样简单 - 就像我说的那样,这延伸到了一大堆组合问题 - 我宁愿看到程序员采用十五分钟学习它,并在有用时应用它,而不是看到废物传播和扩展。
许多答案和评论围绕生成所有这些组合(并计算它们)。我个人并没有多少用处,因为这套装置已经众所周知。实际上,您通常希望生成例如小子集 - 对,三元组或更大的集合 - 或满足某些标准的子集集合;例如,您可能希望生成十对这样的数字,每个九位数使用两次,但不能在一对中使用。我的种子方法很容易实现;而不是十进制表示,而是使用连续的种子值(0到362879,包括0和362879)。
也就是说,在C中生成(并打印)给定字符串的所有permutations是直截了当的:
str
置换函数是递归的,但其最大递归深度是字符串长度。对函数的调用总数为 a ( N ),其中 N 是字符串的长度, a < / em>( n )= n ⋅ a ( n -1)+1(序列{{3在这种特殊情况下调用623530。通常, a ( n )≤(1- e ) n !,即 a < / em>( n )&lt; 1.7183 n !,因此调用次数为 O ( N ! ),关于项目数量的阶乘。与调用相比,循环体的迭代次数少了623529次。
逻辑相当简单,使用与第一个代码片段相同的数组方法,除了这次&#34;修剪掉&#34;数组的一部分实际上用于存储置换字符串。换句话说,我们将剩下的每个字符与下一个字符交换为trimemd(或预先添加到最后的字符串),进行递归调用,并恢复两个字符。因为每次递归调用后每个修改都被撤消,所以缓冲区中的字符串在调用之后与之前相同。就好像它从未被修改过一样。
上述实现确实假设一个字节的字符(并且不能正确地使用例如多字节UTF-8序列)。如果要使用Unicode字符或某些其他多字节字符集中的字符,则应使用宽字符。除了类型更改和更改函数以打印字符串之外,不需要进行任何其他更改。
答案 1 :(得分:14)
给定一个数字数组,可以用一个相当简单的函数生成这些数字的下一个排列(让我们调用该函数nextPermutation
)。如果数组以排序顺序的所有数字开头,则nextPermutation
函数将按升序生成所有可能的排列。例如,此代码
int main( void )
{
int array[] = { 1, 2, 3 };
int length = sizeof(array) / sizeof(int);
printf( "%d\n", arrayToInt(array, length) ); // show the initial array
while ( nextPermutation(array, length) )
printf( "%d\n", arrayToInt(array, length) ); // show the permutations
}
将生成此输出
123
132
213
231
312
321
如果您将array
更改为
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
然后代码将按升序生成并显示这9个数字的所有362880个排列。
nextPermutation
功能有三个步骤
x
),后跟一个更大的数字y
的第一个数字(称之为x
),并交换x
和y
y
现在是x
所在的位置,y
右侧的所有数字都按降序排列,交换它们以便它们按升序排列让我举一个例子来说明。假设数组按此顺序具有数字
1 9 5 4 8 7 6 3 2
第一步会找到4
。由于8 7 6 3 2
按降序排列,4
是第一个数字(从数组末尾开始),后跟一个更大的数字。
第二步会找到6
,因为6
是第一个数字(从数组末尾开始),大于4
。交换4
和6
之后,数组看起来像这样
1 9 5 6 8 7 4 3 2
请注意6
右侧的所有数字都按降序排列。交换6
和4
并没有改变数组中最后五个数字按降序排列的事实。
最后一步是在6
之后交换数字,使它们全部按升序排列。由于我们知道数字是按降序排列的,因此我们需要做的就是将8
与2
交换,将7
与3
交换。结果数组是
1 9 5 6 2 3 4 7 8
因此,给定数字的任何排列,函数将通过交换几个数字来找到下一个排列。唯一的例外是最后一个排列,它具有相反顺序的所有数字,即9 8 7 6 5 4 3 2 1
。在这种情况下,步骤1失败,函数返回0表示没有更多的排列。
所以这是nextPermutation
函数
int nextPermutation( int array[], int length )
{
int i, j, temp;
// starting from the end of the array, find the first number (call it 'x')
// that is followed by a larger number
for ( i = length - 2; i >= 0; i-- )
if ( array[i] < array[i+1] )
break;
// if no such number was found (all the number are in reverse order)
// then there are no more permutations
if ( i < 0 )
return 0;
// starting from the end of the array, find the first number (call it 'y')
// that is larger than 'x', and swap 'x' and 'y'
for ( j = length - 1; j > i; j-- )
if ( array[j] > array[i] )
{
temp = array[i];
array[i] = array[j];
array[j] = temp;
break;
}
// 'y' is now where 'x' was, and all of the numbers to the right of 'y'
// are in descending order, swap them so that they are in ascending order
for ( i++, j = length - 1; j > i; i++, j-- )
{
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return 1;
}
请注意,nextPermutation
函数适用于任何数字数组(数字不需要是连续的)。例如,如果起始数组是
int array[] = { 2, 3, 7, 9 };
然后nextPermutation
函数将找到2,3,7和9的所有排列。
为了完整起见,这是arrayToInt
函数中使用的main
函数。此功能仅用于演示目的。它假定数组只包含单个数字,并且不会检查溢出。如果int
至少为32位,它将适用于9位数字。
int arrayToInt( int array[], int length )
{
int result = 0;
for ( int i = 0; i < length; i++ )
result = result * 10 + array[i];
return result;
}
由于似乎对该算法的性能有一些兴趣,这里有一些数字:
length= 2 perms= 2 (swaps= 1 ratio=0.500) time= 0.000msec length= 3 perms= 6 (swaps= 7 ratio=1.167) time= 0.000msec length= 4 perms= 24 (swaps= 34 ratio=1.417) time= 0.000msec length= 5 perms= 120 (swaps= 182 ratio=1.517) time= 0.001msec length= 6 perms= 720 (swaps= 1107 ratio=1.538) time= 0.004msec length= 7 perms= 5040 (swaps= 7773 ratio=1.542) time= 0.025msec length= 8 perms= 40320 (swaps= 62212 ratio=1.543) time= 0.198msec length= 9 perms= 362880 (swaps= 559948 ratio=1.543) time= 1.782msec length=10 perms= 3628800 (swaps= 5599525 ratio=1.543) time= 16.031msec length=11 perms= 39916800 (swaps= 61594835 ratio=1.543) time= 170.862msec length=12 perms=479001600 (swaps=739138086 ratio=1.543) time=2036.578msec
测试的CPU是2.5Ghz Intel i5处理器。该算法每秒产生大约2亿个排列,并且生成9个数字的所有排列所需的时间不到2毫秒。
同样令人感兴趣的是,平均而言,该算法每次排列仅需要大约1.5次交换。有一半的时间,算法只是交换数组中的最后两个数字。在24个案例中的11个案例中,该算法进行了两次交换。因此,只有24个案例中的1个算法需要两次以上的交换。
最后,我尝试了使用以下两个数组的算法
int array[] = { 1, 2, 2, 3 }; // generates 12 permutations
int array[] = { 1, 2, 2, 3, 3, 3, 4 }; // generates 420 permutations
排列的数量与预期的一样,输出似乎是正确的,所以如果数字不是唯一的话,算法似乎也有效。
答案 2 :(得分:12)
递归在这里很好用。
#include <stdio.h>
void uniq_digits(int places, int prefix, int mask) {
if (!places) {
printf("%d\n", prefix);
return;
}
for (int i = 0; i < 10; i++) {
if (prefix==0 && i==0) continue;
if ((1<<i)&mask) continue;
uniq_digits(places-1, prefix*10+i, mask|(1<<i));
}
}
int main(int argc, char**argv) {
uniq_digits(9, 0, 0);
return 0;
}
答案 3 :(得分:8)
这是一个简单的程序,它将打印一组字符的所有排列。您可以轻松转换它以生成所需的所有数字:
#include <stdio.h>
static int step(const char *str, int n, const char *set) {
char buf[n + 2];
int i, j, count;
if (*set) {
/* insert the first character from `set` in all possible
* positions in string `str` and recurse for the next
* character.
*/
for (count = 0, i = n; i >= 0; i--) {
for (j = 0; j < i; j++)
buf[j] = str[j];
buf[j++] = *set;
for (; j <= n; j++)
buf[j] = str[j - 1];
buf[j] = '\0';
count += step(buf, n + 1, set + 1);
}
} else {
printf("%s\n", str);
count = 1;
}
return count;
}
int main(int argc, char **argv) {
int total = step("", 0, argc > 1 ? argv[1] : "123456789");
printf("%d combinations\n", total);
return 0;
}
它使用递归但不使用位掩码,可用于任何字符集。它还计算排列的数量,因此您可以验证它是否为一组n个字符生成阶乘(n)排列。
答案 4 :(得分:7)
这里有很多长代码。最好多思考,少编码。
我们希望在没有浪费精力的情况下完全生成每种可能性。事实证明,这是可能的,每发出一个数字只需要一定量的努力。
如果没有代码,你会怎么做?获取10张卡并在其上写入0到9的数字。在桌面上画一排9个方格。选一张卡片。把它放在第一个方格中,另一个放在第二个方格中等等。当你选择9时,你就得到了你的第一个数字。现在取出最后一张卡并用每种可能的替代品替换它。 (在这种情况下,只有1个。)每次填充所有方格时,你都有另一个数字。如果你已经完成了最后一个方格的所有替代方法,那么请在最后一个方格中进行。重复最后一个方法等,直到你考虑了所有方框的所有替代方案。
编写一个简洁的程序来做这个就是选择简单的数据结构。对9行的行使用一个字符数组。
为这组卡片使用另一个阵列。要从存储在数组A [0 .. N-1]中的大小N集合中删除元素,我们使用旧技巧。假设您要删除的元素是A [I]。将A [I]的值保存到侧面。然后复制最后一个元素A [N-1]&#34; down,&#34;覆盖A [I]。新集合为A [0 .. N-2]。这很好,因为我们不关心一组中的顺序。
其余的是使用递归思维来枚举所有可能的替代方案。如果我知道如何从大小为M的字符集中找到所有选择到大小为N的字符串,那么要获得算法,只需为第一个字符串位置选择每个可能的字符,然后重复选择N-1的其余部分。其余大小为M-1的字符。我们得到了一个很好的12行函数:
#include <stdio.h>
// Select each element from the given set into buf[pos], then recur
// to select the rest into pos+1... until the buffer is full, when
// we print it.
void select(char *buf, int pos, int len, char *set, int n_elts) {
if (pos >= len)
printf("%.*s\n", len, buf); // print the full buffer
else
for (int i = 0; i < n_elts; i++) {
buf[pos] = set[i]; // select set[i] into buf[pos]
set[i] = set[n_elts - 1]; // remove set[i] from the set
select(buf, pos + 1, len, set, n_elts - 1); // recur to pick the rest
set[n_elts - 1] = set[i]; // undo for next iteration
set[i] = buf[pos];
}
}
int main(void) {
char buf[9], set[] = "0123456789";
select(buf, 0, 9, set, 10); // select 9 characters from a set of 10
return 0;
}
你没有提到是否可以在第一个位置加零。假设它不是。由于我们很好地理解了算法,因此很容易避免在第一个位置选择零。如果pos为0和0,可以通过观察C中!pos
的值为1来跳过这种可能性。如果你不喜欢这个略显模糊的习语,试试(pos == 0 ? 1 : 0)
作为一个更易读的替代品:
#include <stdio.h>
void select(char *buf, int pos, int len, char *set, int n_elts) {
if (pos >= len)
printf("%.*s\n", len, buf);
else
for (int i = !pos; i < n_elts; i++) {
buf[pos] = set[i];
set[i] = set[n_elts - 1];
select(buf, pos + 1, len, set, n_elts - 1);
set[n_elts - 1] = set[i];
set[i] = buf[pos];
}
}
int main(void) {
char buf[9], set[] = "0123456789";
select(buf, 0, 9, set, 10);
return 0;
}
答案 5 :(得分:6)
而不是10个变量,我会为10个数字中的每个数字创建一个具有位设置(和可测试)的变量。然后,您只需要对每个数字对应的位进行循环设置(并测试)。像这样:
int ok = 1;
unsigned bits = 0;
int digit;
unsigned powers10 = 1;
for (digit = 0; digit < 10; ++digit) {
unsigned bit = 1 << ((num / powers10) % 10);
if ((bits & bit) != 0) {
ok = 0;
break;
}
bits |= bit;
powers10 *= 10;
}
if (ok) {
printf("%d\n", num);
}
完成程序(丢弃不必要的#include
行):
#include <stdio.h>
int main(void)
{
int indx;
int num;
for(indx = 123456789; indx <= 987654321; indx++)
{
num = indx;
int ok = 1;
unsigned bits = 0;
int digit;
unsigned powers10 = 1;
for (digit = 0; digit < 9; ++digit) {
unsigned bit = 1 << ((num / powers10) % 10);
if ((bit == 1) || ((bits & bit) != 0)) {
ok = 0;
break;
}
bits |= bit;
powers10 *= 10;
}
if (ok) {
printf("%d\n", num);
}
}
return 0;
}
OP在我上班时澄清了他的问题,而我并没有专注于缺少所要求的零。 (现在更新响应)。这产生了预期的362880种组合。
然而 - 有一个评论是关于一个答案最快,这促使后续。有(计算这一个)三个可比较的答案。快速检查:
real 0m0.951s user 0m0.894s sys 0m0.056s
real 0m49.108s user 0m49.041s sys 0m0.031s
real 1m27.597s user 1m27.476s sys 0m0.051s
答案 6 :(得分:6)
你可以使用一个掩码来设置标志,标志是否已经在数字中看到了数字。像这样:
int mask = 0x0, j;
for(j= 1; j<=9; j++){
if(mask & 1<<(input%10))
return 0;
else
mask |= 1<<(input%10);
input /= 10;
}
return !(mask & 1);
完整的计划:
#include <stdio.h>
int check(int input)
{
int mask = 0x0, j;
for(j= 1; j<=9; j++){
if(mask & 1<<(input%10))
return 0;
else
mask |= 1<<(input%10);
input /= 10;
}
/* At this point all digits are unique
We're not interested in zero, though */
return !(mask & 1);
}
int main()
{
int indx;
for( indx = 123456789; indx <=987654321; indx++){
if( check(indx) )
printf("%d\n",indx);
}
}
...编辑
或者你可以对数组做同样的事情:
int check2(int input)
{
int j, arr[10] = {0,0,0,0,0,0,0,0,0,0};
for(j=1; j<=9; j++) {
if( (arr[input%10]++) || (input%10 == 0) )
return 0;
input /= 10;
}
return 1;
}
答案 7 :(得分:6)
这是一种方法 - 从一组唯一数字开始,然后随机地将它们混洗:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
int main( void )
{
char digits[] = "123456789";
srand( time( NULL ) );
size_t i = sizeof digits - 1;
while( i )
{
size_t j = rand() % i;
char tmp = digits[--i];
digits[i] = digits[j];
digits[j] = tmp;
}
printf( "number is %s\n", digits );
return 0;
}
一些示例输出:
john@marvin:~/Development/snippets$ ./nine
number is 249316578
john@marvin:~/Development/snippets$ ./nine
number is 928751643
john@marvin:~/Development/snippets$ ./nine
number is 621754893
john@marvin:~/Development/snippets$ ./nine
number is 317529864
请注意,这些是唯一十进制数字的字符串,而不是数字值;如果你想要相应的整数值,你需要进行像
这样的转换long val = strtol( digits, NULL, 10 );
答案 8 :(得分:6)
检查此代码。
#include<stdio.h>
//it can be done by recursion
void func(int *flag, int *num, int n){ //take 'n' to count the number of digits
int i;
if(n==9){ //if n=9 then print the number
for(i=0;i<n;i++)
printf("%d",num[i]);
printf("\n");
}
for(i=1;i<=9;i++){
//put the digits into the array one by one and send if for next level
if(flag[i-1]==0){
num[n]=i;
flag[i-1]=1;
func(flag,num,n+1);
flag[i-1]=0;
}
}
}
//here is the MAIN function
main(){
int i,flag[9],num[9];
for(i=0;i<9;i++) //take a flag to avoid repetition of digits in a number
flag[i]=0; //initialize the flags with 0
func(flag,num,0); //call the function
return 0;
}
如果您有任何疑问,请随时提出。
答案 9 :(得分:6)
我推荐Nominal Animal的answer,但如果您只是生成此值以便打印出来,您可以删除部分工作,同时获得更通用的功能常规使用相同的方法:
char *shuffle( char *digit, int digits, int count, unsigned int seed )
{
//optional: do some validation on digit string
// ASSERT(digits == strlen(digit));
//optional: validate seed value is reasonable
// for(unsigned int badseed=1, x=digits, y=count; y > 0; x--, y--)
// badseed *= x;
// ASSERT(seed < badseed);
char *work = digit;
while(count--)
{
int i = seed % digits;
seed /= digits--;
unsigned char selectedDigit = work[i];
work[i] = work[0];
work[0] = selectedDigit;
work++;
}
work[0] = 0;
//seed should be zero here, else the seed contained extra information
return digit;
}
此方法对传入的数字具有破坏性,实际上不必是数字,或者就此而言是唯一的。
如果您希望以排序递增顺序生成的输出值有更多工作:
char *shuffle_ordered( char *digit, int digits, int count, unsigned int seed )
{
char *work = digit;
int doneDigits = 0;
while(doneDigits < count)
{
int i = seed % digits;
seed /= digits--;
unsigned char selectedDigit = work[i];
//move completed digits plus digits preceeding selectedDigit over one place
memmove(digit+1,digit,doneDigits+i);
digit[0] = selectedDigit;
work++;
}
work[0] = 0;
//seed should be zero here, else the seed contained extra information
return digit;
}
在任何一种情况下都会这样调用:
for(unsigned int seed = 0; seed < 16*15*14; ++seed)
{
char work[] = "0123456789ABCDEF";
printf("seed=%d -> %s\n",shuffle_ordered(work,16,3,seed));
}
这应打印出三位十六进制值的有序列表,没有重复的数字:
seed 0 -> 012
seed 1 -> 013
...
seed 3358 -> FEC
seed 3359 -> FED
我不知道你用这些精心设计的数字序列实际上在做什么 。如果一些可怜的维持工程师不得不在你后面修复一些错误,我建议订购版本,因为人类更容易将种子从/转换为序列值。 way / p>
答案 10 :(得分:5)
使用嵌套的for loops
,这是一个有点丑陋但非常快速的解决方案。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define NINE_FACTORIAL 362880
int main(void) {
//array where numbers would be saved
uint32_t* unique_numbers = malloc( NINE_FACTORIAL * sizeof(uint32_t) );
if( !unique_numbers ) {
printf("Could not allocate memory for the Unique Numbers array.\n");
exit(1);
}
uint32_t n = 0;
int a,b,c,d,e,f,g,h,i;
for(a = 1; a < 10; a++) {
for(b = 1; b < 10; b++) {
if (b == a) continue;
for(c = 1; c < 10; c++) {
if(c==a || c==b) continue;
for(d = 1; d < 10; d++) {
if(d==a || d==b || d==c) continue;
for(e = 1; e < 10; e++) {
if(e==a || e==b || e==c || e==d) continue;
for(f = 1; f < 10; f++) {
if (f==a || f==b || f==c || f==d || f==e)
continue;
for(g = 1; g < 10; g++) {
if(g==a || g==b || g==c || g==d || g==e
|| g==f) continue;
for(h = 1; h < 10; h++) {
if (h==a || h==b || h==c || h==d ||
h==e || h==f || h==g) continue;
for(i = 1; i < 10; i++) {
if (i==a || i==b || i==c || i==d ||
i==e || i==f || i==g || i==h) continue;
// print the number or
// store the number in the array
unique_numbers[n++] = a * 100000000
+ b * 10000000
+ c * 1000000
+ d * 100000
+ e * 10000
+ f * 1000
+ g * 100
+ h * 10
+ i;
}
}
}
}
}
}
}
}
}
// do stuff with unique_numbers array
// n contains the number of elements
free(unique_numbers);
return 0;
}
使用某些宏也是一样。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define l_(b,n,c,p,f) { int i; for(i = 1; i < 10; i++) { \
int j,r=0; for(j=0;j<p;j++){if(i == c[j]){r=1;break;}} \
if(r) continue; c[p] = i; f } }
#define l_8(b,n,c,p) { \
int i; for(i=1; i< 10; i++) {int j, r=0; \
for(j=0; j<p; j++) {if(i == c[j]) {r = 1; break;}} \
if(r)continue; b[n++] = c[0] * 100000000 + c[1] * 10000000 \
+ c[2] * 1000000 + c[3] * 100000 + c[4] * 10000 \
+ c[5] * 1000 + c[6] * 100 + c[7] * 10 + i; } }
#define l_7(b,n,c,p) l_(b,n,c,p, l_8(b,n,c,8))
#define l_6(b,n,c,p) l_(b,n,c,p, l_7(b,n,c,7))
#define l_5(b,n,c,p) l_(b,n,c,p, l_6(b,n,c,6))
#define l_4(b,n,c,p) l_(b,n,c,p, l_5(b,n,c,5))
#define l_3(b,n,c,p) l_(b,n,c,p, l_4(b,n,c,4))
#define l_2(b,n,c,p) l_(b,n,c,p, l_3(b,n,c,3))
#define l_1(b,n,c,p) l_(b,n,c,p, l_2(b,n,c,2))
#define get_unique_numbers(b,n,c) do {int i; for(i=1; i<10; i++) { \
c[0] = i; l_1(b,n,c,1) } } while(0)
#define NINE_FACTORIAL 362880
int main(void) {
//array where numbers would be saved
uint32_t* unique_numbers = malloc( NINE_FACTORIAL * sizeof(uint32_t) );
if( !unique_numbers ) {
printf("Could not allocate memory for the Unique Numbers array.\n");
exit(1);
}
int n = 0;
int current_number[8] = {0};
get_unique_numbers(unique_numbers, n, current_number);
// do stuff with unique_numbers array
// NINE_FACTORIAL is the number of elements
free(unique_numbers);
return 0;
}
我确信有更好的方法来编写这些宏,但这是我能想到的。
答案 11 :(得分:5)
编辑:经过进一步分析后,更多的递归展开并且仅在设置位上进行迭代导致显着改善,在我的测试中大约快了五倍。这是使用OUTPUT
UNSET 测试来比较算法速度而不是控制台输出,起点是uniq_digits9
:
int counter=0;
int reps=0;
void show(int x)
{
#ifdef OUTPUT
printf("%d\n", x);
#else
counter+=x;
++reps;
#endif
}
int bit_val(unsigned int v)
{
static const int MultiplyDeBruijnBitPosition2[32] =
{
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
return MultiplyDeBruijnBitPosition2[(unsigned int)(v * 0x077CB531U) >> 27];
}
void uniq_digits1(int prefix, unsigned int used) {
show(prefix*10+bit_val(~used));
}
void uniq_digits2(int prefix, unsigned int used) {
int base=prefix*10;
unsigned int unused=~used;
while (unused) {
unsigned int diff=unused & (unused-1);
unsigned int bit=unused-diff;
unused=diff;
uniq_digits1(base+bit_val(bit), used|bit);
}
}
void uniq_digits3(int prefix, unsigned int used) {
int base=prefix*10;
unsigned int unused=~used;
while (unused) {
unsigned int diff=unused & (unused-1);
unsigned int bit=unused-diff;
unused=diff;
uniq_digits2(base+bit_val(bit), used|bit);
}
}
void uniq_digits4(int prefix, unsigned int used) {
int base=prefix*10;
unsigned int unused=~used;
while (unused) {
unsigned int diff=unused & (unused-1);
unsigned int bit=unused-diff;
unused=diff;
uniq_digits3(base+bit_val(bit), used|bit);
}
}
void uniq_digits5(int prefix, unsigned int used) {
int base=prefix*10;
unsigned int unused=~used;
while (unused) {
unsigned int diff=unused & (unused-1);
unsigned int bit=unused-diff;
unused=diff;
uniq_digits4(base+bit_val(bit), used|bit);
}
}
void uniq_digits6(int prefix, unsigned int used) {
int base=prefix*10;
unsigned int unused=~used;
while (unused) {
unsigned int diff=unused & (unused-1);
unsigned int bit=unused-diff;
unused=diff;
uniq_digits5(base+bit_val(bit), used|bit);
}
}
void uniq_digits7(int prefix, unsigned int used) {
int base=prefix*10;
unsigned int unused=~used;
while (unused) {
unsigned int diff=unused & (unused-1);
unsigned int bit=unused-diff;
unused=diff;
uniq_digits6(base+bit_val(bit), used|bit);
}
}
void uniq_digits8(int prefix, unsigned int used) {
int base=prefix*10;
unsigned int unused=~used;
while (unused) {
unsigned int diff=unused & (unused-1);
unsigned int bit=unused-diff;
unused=diff;
uniq_digits7(base+bit_val(bit), used|bit);
}
}
void uniq_digits9() {
unsigned int used=~((1<<10)-1); // set all bits except 0-9
#ifndef INCLUDE_ZEROS
used |= 1;
#endif
for (int i = 1; i < 10; i++) {
unsigned int bit=1<<i;
uniq_digits8(i,used|bit);
}
}
简要说明:
有9个数字,第一个数字不能从零开始,所以第一个数字可以是1到9,其余数字可以是0到9
如果我们取一个数字,X并乘以10,它会移动一个地方。所以,5变为50.添加一个数字,比如说3表示53,然后乘以10得到520,然后加2,依此类推所有9位数。
现在需要一些存储空间来跟踪使用的数字,这样它们就不会被重复。可以使用10个真/假变量:used_0_p
,used_1_P
,....但是,这是低效的,因此可以将它们放在数组中:used_p[10]
。但是,每次在下一个地方打电话之前都需要复制它,这样它就可以将它重置为下一个数字,否则一旦所有位置都被填充,第一次数组将全部为真,并且不能计算其他组合。
但是,还有更好的方法。使用int
的位作为数组。 X & 1
,X & 2
,X & 4
,X & 8
等(1<<X)
。此序列可以表示为&
或取第一位并将其移至X次。
|
用于测试位,(1<<i)&used
用于设置它们。在每个循环中,我们测试该位是否被使用prefix*10+i
并且如果是的话则跳过。在下一个地方,我们移动每个数字used|(1<<i)
的数字并将该数字设置为使用Y & (Y-1)
编辑中循环的说明
循环计算~
,它将最低设置位置零。通过取原始值并减去结果,差值是最低位。这将循环次数与位数相同:3,265,920次而不是900,000,000次。从使用切换到未使用只是void uniq_digits(int places, int prefix, int used) {
if (!places) {
printf("%d\n", prefix);
return;
}
--places;
int base=prefix*10;
for (int i = 0; i < 10; i++)
{
if ((1<<i)&used) continue;
uniq_digits(places, base+i, used|(1<<i));
}
}
int main(int argc, char**argv) {
const int num_digits=9;
// unroll top level to avoid if for every iteration
for (int i = 1; i < 10; i++)
{
uniq_digits(num_digits-1, i, 1 << i);
}
return 0;
}
运算符,因为设置比取消设置更有效,所以翻转是有意义的
从2的幂到log2取自:https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog。该站点还详细介绍了循环机制:https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2
将原文移到底部:
这对于评论来说太长了,但是通过从函数中删除零处理可以使This answer更快一些:(请参阅编辑以获得最快的答案)
<div class="project project1">
<div class="image"><img src="your/image.jpg"></div>
<div class="project-hover project-hover1">Hover text #1</div>
</div>
答案 12 :(得分:5)
一种简单的方法是创建一个包含九个不同值的数组,将其混洗,然后打印混洗数组。根据需要重复多次。例如,使用标准rand()
函数作为改组的基础......
#include <stdlib.h> /* for srand() and rand */
#include <time.h> /* for time() */
#include <stdio.h>
#define SIZE 10 /* size of working array. There are 10 numeric digits, so .... */
#define LENGTH 9 /* number of digits we want to output. Must not exceed SIZE */
#define NUMBER 12 /* number of LENGTH digit values we want to output */
void shuffle(char *buffer, int size)
{
int i;
char temp;
for (i=size-1; i>0; --i)
{
/* not best way to get a random value of j in [0, size-1] but
sufficient for illustrative purposes
*/
int j = rand()%size;
/* swap buffer[i] and buffer[j] */
temp = buffer[i];
buffer[i] = buffer[j];
buffer[j] = temp;
}
}
void printout(char *buffer, int length)
{
/* this assumes SIZE <= 10 and length <= SIZE */
int i;
for (i = 0; i < length; ++i)
printf("%d", (int)buffer[i]);
printf("\n");
}
int main()
{
char buffer[SIZE];
int i;
srand((unsigned)time(NULL)); /* seed for rand(), once and only once */
for (i = 0; i < SIZE; ++i) buffer[i] = (char)i; /* initialise buffer */
for (i = 0; i < NUMBER; ++i)
{
/* keep shuffling until first value in buffer is non-zero */
do shuffle(buffer, SIZE); while (buffer[0] == 0);
printout(buffer, LENGTH);
}
return 0;
}
这会将多行打印到stdout
,每行包含9个唯一数字。请注意,这不会阻止重复。
答案 13 :(得分:0)
广泛使用位的迭代版本
请注意array
可以更改为任何类型,并按任意顺序设置
这将按照给定的顺序“计算”数字
有关更多解释,请查看我的第一个答案(灵活性较低但速度更快)https://stackoverflow.com/a/31928246/2963099
为了使其迭代,需要数组来保持每个级别的状态
对于优化器无法弄清楚的地方,这也进行了相当多的优化
int bit_val(unsigned int v) {
static const int MultiplyDeBruijnBitPosition2[32] = {
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
return MultiplyDeBruijnBitPosition2[(unsigned int)(v * 0x077CB531U) >> 27];
}
void uniq_digits(const int array[], const int length) {
unsigned int unused[length-1]; // unused prior
unsigned int combos[length-1]; // digits untried
int digit[length]; // printable digit
int mult[length]; // faster calcs
mult[length-1]=1; // start at 1
for (int i = length-2; i >= 0; --i)
mult[i]=mult[i+1]*10; // store multiplier
unused[0]=combos[0]=((1<<(length))-1); // set all bits 0-length
int depth=0; // start at top
digit[0]=0; // start at 0
while(1) {
if (combos[depth]) { // if bits left
unsigned int avail=combos[depth]; // save old
combos[depth]=avail & (avail-1); // remove lowest bit
unsigned int bit=avail-combos[depth]; // get lowest bit
digit[depth+1]=digit[depth]+mult[depth]*array[bit_val(bit)]; // get associated digit
unsigned int rest=unused[depth]&(~bit); // all remaining
depth++; // go to next digit
if (depth!=length-1) { // not at bottom
unused[depth]=combos[depth]=rest; // try remaining
} else {
show(digit[depth]+array[bit_val(rest)]); // print it
depth--; // stay on same level
}
} else {
depth--; // go back up a level
if (depth < 0)
break; // all done
}
}
}
一些时间仅使用1到9次,1000次重复:
答案 14 :(得分:0)
派对有点晚了,但很快(30毫秒)......
DataTable.Rows
.OfType<DataRow>()
.Select(r => (double)r[header])
.ToList()
.ForEach(v => {
var isEmpty = Double.IsNaN(v);
var value = new double[] { v == Double.NaN ? 0 : v };
series.Points.Add(new DataPoint() {
IsEmpty = isEmpty,
YValues = value
});
});
答案 15 :(得分:0)
制作一个包含10个元素的列表,其值均为0-9。按rand()/ w当前列表长度拉出随机元素,直到获得所需的位数。