我正在尝试做一些非常基本的事情,它将循环遍历数组的每个可能的排列。
实际上这是在装配中完成的,但我会在C中解释它。
基本上,假设我们有一个数组uint8_t *data=malloc(10);
我想创建一个算法来打印数组data
中每个字节的可能组合。
是的,我知道它会很慢(并且有许多值),而且我并不是要求真正复杂的优化版本。我只是想找一些我可以在计算机上运行的东西找到某些符合某些条件的值的蛮力型东西。
(注意,我说排列因为[0,1,2]不应该与[2,1,0]相同)
编辑: 另外,尽量不要使用太多的libc函数,因为我将它转换为只有512字节的独立引导加载程序。
我知道我知道如何做到这一点,但对于我的生活,我无法让算法在我的脑海里工作!
答案 0 :(得分:4)
好吧,整件事情是徒劳的(请参阅我对该问题的评论),但无论如何你都去了(x86_64 AT& T样式汇编,假设是AMD系统V调用约定)。我只是在这里写这个没有测试,所以它完全有可能它有bug。尽管如此,代码的基本操作应该是完全清楚的。
我只是在两个64位寄存器中运行80位字段分割的所有可能性,而不是在内存中的80位缓冲区上运行。您检查条件的例程可以将它们存储到内存中,如果您真的想要,则可以uint8_t
访问该内存。
push r12
push r13
xor r12, r12 // zero out low 64 bits of our "buffer" in register
xor r13, r13 // zero out high 16 bits of our "buffer"
loop:
// Copy the current array value into rsi:rdi and call whatever routine you're
// using to check for magic conditions. This routine's copy (in r13:r12)
// should be unaffected if you're obeying the System V calling conventions.
mov r12, rdi
mov r13, rsi
call _doSomethingWithValue
// Increment r13:r12 to get the next value. We only need to worry about r13
// if the increment of r12 wraps around to zero.
inc r12
jnz loop
inc r13
// Check for the termination condition, though you'll never hit it =)
cmp $0x10000, r13
jne loop
// We don't actually need to clean up; the apocalypse will come and there
// won't be electricity to run the computer before it reaches this point of
// the program. Nonetheless, let's be exhaustively correct.
pop r13
pop r12
答案 1 :(得分:3)
你的问题遭受了一个奇怪的术语混淆。根据您的描述,您似乎希望生成所有可能的10元组无符号8位值。这些不是“排列”,所有这些都与生成排列无关。
生成所有可能的10元uint8_t
值的代码很容易想出来。例如,以下简单代码将执行此操作
#define N 10u
uint8_t data[N] = { 0 };
unsigned i;
do {
/* Process the current 10-typle in `data` array */
/* in any way you want do */
/* Generate next tuple */
for (i = 0; i < N && ++data[i] == 0; ++i);
} while (i < N);
这只不过是80位小端数的循环增量。
当然,正如其他人已经指出的那样,从实际的角度来看,这件事所花费的时间是完全无用的。
答案 2 :(得分:2)
我建议您阅读,
唐纳德克努特。计算机程序设计艺术,第4卷,分册2:生成所有元组和排列。答案 3 :(得分:0)
这个问题有一个典型的递归方法,类似于以下内容:
#include <stdio.h>
void print(const uint8_t *v, const int size)
{
if (v != 0) {
for (int i = 0; i < size; i++) {
printf("%4d", v[i] );
}
printf("\n");
}
} // print
void visit(uint8_t *Value, int N, int k)
{
static level = -1;
level = level+1; Value[k] = level;
if (level == N)
print(Value, N);
else
for (int i = 0; i < N; i++)
if (Value[i] == 0)
visit(Value, N, i);
level = level-1; Value[k] = 0;
}
main()
{
const int N = 4;
uint8_t Value[N];
for (int i = 0; i < N; i++) {
Value[i] = 0;
}
visit(Value, N, 0);
}
示例来自link,其中还有其他方法。它背后的理论很简单..如果你需要我可以进一步解释算法,但它是非常自我解释的。
答案 4 :(得分:0)
查看this algorithm以生成M个项目中的N个组合。对于N的组合选择N,只需使用inittwiddle(N,N,p);
int twiddle(x, y, z, p)
int *x, *y, *z, *p;
{
register int i, j, k;
j = 1;
while(p[j] <= 0)
j++;
if(p[j-1] == 0)
{
for(i = j-1; i != 1; i--)
p[i] = -1;
p[j] = 0;
*x = *z = 0;
p[1] = 1;
*y = j-1;
}
else
{
if(j > 1)
p[j-1] = 0;
do
j++;
while(p[j] > 0);
k = j-1;
i = j;
while(p[i] == 0)
p[i++] = -1;
if(p[i] == -1)
{
p[i] = p[k];
*z = p[k]-1;
*x = i-1;
*y = k-1;
p[k] = -1;
}
else
{
if(i == p[0])
return(1);
else
{
p[j] = p[i];
*z = p[i]-1;
p[i] = 0;
*x = j-1;
*y = i-1;
}
}
}
return(0);
}
void inittwiddle(m, n, p)
int m, n, *p;
{
int i;
p[0] = n+1;
for(i = 1; i != n-m+1; i++)
p[i] = 0;
while(i != n+1)
{
p[i] = i+m-n;
i++;
}
p[n+1] = -2;
if(m == 0)
p[1] = 1;
}
/************************
Here is a sample use of twiddle() and inittwiddle():
#define N 5
#define M 2
#include <stdio.h>
void main()
{
int i, x, y, z, p[N+2], b[N];
inittwiddle(M, N, p);
for(i = 0; i != N-M; i++)
{
b[i] = 0;
putchar('0');
}
while(i != N)
{
b[i++] = 1;
putchar('1');
}
putchar('\n');
while(!twiddle(&x, &y, &z, p))
{
b[x] = 1;
b[y] = 0;
for(i = 0; i != N; i++)
putchar(b[i]? '1': '0');
putchar('\n');
}
}
************************/
这篇文章的答案也可以帮助你Algorithm to return all combinations of k elements from n
答案 5 :(得分:0)
如果您使用的是C ++,
#include <algorithm>
#include <iterator>
#include <iostream>
#include <numeric>
int main() {
int N;
std::cin >> N;
std::vector<int> data(N);
std::fill(data.begin(), data.end(), 1);
std::partial_sum(data.begin(), data.end(), data.begin());
do {
std::copy(data.begin(), data.end(),
std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
} while (std::next_permutation(data.begin(), data.end()));
return 0;
}
如果输入3
,则输出
1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1
请参阅Next permutation: When C++ gets it right了解std::next_permutation
的工作原理。
将此翻译为普通C,
#include <stdio.h>
#include <stdlib.h>
int main() {
int i, N, *data;
scanf("%d", &N);
data = malloc(N);
for (i = 0; i < N; i++) data[i] = i + 1;
while (1) {
int j, temp;
for (i = 0; i < N; i++) printf("%d ", data[i]);
printf("\n");
for (i = N - 1; i > 0 && data[i] < data[i - 1]; i--);
if (i <= 0) break;
for (j = N; data[i - 1] >= data[--j];);
temp = data[i - 1], data[i - 1] = data[j], data[j] = temp;
for (j = N - 1; i < j; i++, j--)
temp = data[i], data[i] = data[j], data[j] = temp;
}
return 0;
}
如果问题不是要求现有数组的排列,而是生成所有可能的数组内容,那么这就容易多了。 (还有更多的组合。)
memset(data, 0, N);
do {
for (i = 0; i < N; i++) printf("%d ", data[i]);
printf("\n");
for (i = 0; i < N && !++data[i++];);
} while (i < N);