我想生成一个整数数组,其中数组中每行和每列的总和是已知的,例如,如果我在c ++中创建一个4乘4的数组,然后伪随机地填充1到100之间的数字:
int array[4][4] = {} ;
for(int x = 0 ; x<4 ; x++){
for(int y = 0 ; y<4 ; y++){
array[x][y] = rand() % 100 + 1 ;
}
}
数组将是:
8, 50, 74, 59
31, 73, 45, 79
24, 10, 41, 66
93, 43, 88, 4
然后如果我将每一行和每一列相加:
int rowSum[4] = {} ;
int columnSum[4] = {} ;
for(int x = 0 ; x < 4; x++){
for(int y = 0 ; y < 4; y++){
rowSum[x] += array[x][y] ;
columnSum[y] += array[x][y] ;
}
}
rowSum为{191,228,141,228}
,columnSum为{156,176,248,208}
我现在要做的是生成任何随机的4x4 1~100数组,它将满足rowSum
和columnSum
我理解有数千个不同的数组将总结为相同的行和列总和,我一直在尝试编写将生成它的代码部分,我真的很感激,如果有人能给我一个线索。
答案 0 :(得分:0)
这里是评论中提到的快速而肮脏的强力搜索。它应该给你一个起点。这是C,而不是C ++。
你从来没有说过,但我假设你希望矩阵元素是非负的。因此,在分配下一个数组元素值时,搜索每个元素a [i] [j]可以在[0..min(rowsum [i],colsum [j])]中具有任何值的空间中搜索被截断我承认没有可能的未来解决方案。
#include <stdio.h>
int a[4][4] = {
{-1, -1, -1, -1},
{-1, -1, -1, -1},
{-1, -1, -1, -1},
{-1, -1, -1, -1}};
int rs[] = {191, 228, 141, 228};
int cs[] = {156, 176, 248, 208};
long long n_solutions = 0;
void research(int i, int j, int ii, int jj, int val);
void print_a(void);
void search(int i, int j) {
if (j < 3) {
if (i < 3) {
int m = rs[i] < cs[j] ? rs[i] : cs[j];
for (int val = 0; val <= m; ++val) research(i, j, i, j + 1, val);
} else {
if (rs[3] >= cs[j]) research(i, j, i, j + 1, cs[j]);
}
} else {
if (i < 3) {
if (cs[j] >= rs[i]) research(i, 3, i + 1, 0, rs[i]);
} else {
if (rs[3] == cs[3]) {
a[3][3] = rs[i];
if (++n_solutions % 100000000 == 0) {
printf("\n%lld\n", n_solutions);
print_a();
}
a[3][3] = -1;
}
}
}
}
void research(int i, int j, int ii, int jj, int val) {
a[i][j] = val; rs[i] -= val; cs[j] -= val;
search(ii, jj);
rs[i] += val; cs[j] += val; a[i][j] = -1;
}
void print_a(void) {
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j)
printf("%4d", a[i][j]);
printf("\n");
}
}
int main(void) {
search(0, 0);
printf("Total solutions: %lld\n", n_solutions);
return 0;
}
例如,如果用这个替换简单的for
循环,你就不会在左上角得到这么多的零:
int b = m / 2; // m/2 can be replaced with any int in [0..m], e.g. a random value.
research(i, j, i, j + 1, b);
for (int d = 1; b + d <= m || b - d >= 0; ++d) {
if (b + d <= m) research(i, j, i, j + 1, b + d);
if (b - d >= 0) research(i, j, i, j + 1, b - d);
}
这是第20亿个解决方案:
78 56 28 29
39 20 84 85
28 34 61 18
11 66 75 76
答案 1 :(得分:0)
很容易找到一些解决方案。
从生成总和为给定值的行开始。它可以简单到使每行中的所有值大约等于rowSum[i]/n
,给予或取一个。当然,此时列的总和将不匹配。
现在修复从最左边到最右边的列。要修复 i 列,请在列条目之间平均分配所需总和与实际总和之间的差异,然后通过在行的i+1...n
项之间平均分配添加值来修复每一行。
比说起来更容易:
void reconstruct (int array[4][4], int rows[4], int cols[4])
{
// build an array with each row adding up to the correct row sum
for (int x = 0; x < 4; x++){
int s = rows[x];
for(int y = 0; y < 4 ; y++){
array[x][y] = s / (4 - y);
s -= array[x][y];
}
}
// adjust columns
for(int y = 0; y < 4 ; y++){
// calculate the adjustment
int s = 0;
for (int x = 0; x < 4; x++){
s += array[x][y];
}
int diff = s - cols[y];
// adjust the column by diff
for (int x = 0; x < 4; x++){
int k = diff / (4 - x);
array[x][y] -= k;
diff -= k;
// adjust the row by k
for (int yy = y + 1; yy < 4; ++yy)
{
int corr = k / (4 - yy);
array[x][yy] += corr;
k -= corr;
}
}
}
}
这个数组当然不是随机的。可以通过随机选择x1,x2,y1,y2和d并执行:
随机化它 array[x1][y1] += d
array[x1][y2] -= d
array[x2][y1] -= d
array[x2][y2] += d
注意结果值不会超出所需范围。