【问题标题】:generate a 2d array of integers from given sums of its rows and columns从给定的行和列的总和生成一个二维整数数组
【发布时间】:2017-09-16 02:47:29
【问题描述】:

我想生成一个整数数组,其中数组中每一行和每一列的总和是已知的,例如,如果我在 c++ 中创建一个 4 x 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}

我现在要做的是生成任何满足rowSumcolumnSum 的随机 4x4 1~100 数组列总和,我一直在尝试编写将生成它的代码部分,如果有人能给我一个线索,我将不胜感激。

【问题讨论】:

  • 嘿嘿。有很多超过几千。我写了一个小蛮力搜索,在搜索空间的早期,它找到了 35 亿个 解决方案。
  • @Gene 解的数量是无限的(实际上受整数大小的限制)。
  • @n.m.我从他的例子中推断出他想要非负样本。
  • @Gene 哦,那显然是有界的。
  • 是的,我理解指数的力量,这就是为什么写一些算法而不是暴力破解应该更容易

标签: c++ arrays random


【解决方案1】:

很容易找到一些解决方案。

从生成总和为给定值的行开始。它可以简单到使每一行中的所有值大约等于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

注意结果值不会超出所需范围。

【讨论】:

  • 谢谢你!我想过将行的总和除以 4,但我不知道如何处理列
【解决方案2】:

这是 cmets 中提到的快速而肮脏的蛮力搜索。它应该给你一个起点。这是 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

【讨论】:

  • 你是用 gpu cuda 还是其他东西计算出来的?因为在普通计算机上,这段代码需要永远产生结果!
  • @user2548447 不。这段代码在我的笔记本电脑上大约 3 秒内计算了 1 亿个解决方案。
  • 你的笔记本电脑规格是什么?
  • @user2548447 旧 Macbook Intel Core i7 2GHz。
【解决方案3】:

如果我们设置矩阵元素必须是非负整数的条件,问题就会变得有趣。这是一个基于贪心算法的O(mn) JAVA解决方案。

int m=rowSum.length;
int n=colSum.length;
int mat[][] = new int[m][n];
        
    for(int i=0;i<m;i++){
        for(int j=0;j<n;j++){
            int tmp=Math.min(rowSum[i],colSum[j]);
            mat[i][j]=tmp;
            rowSum[i]-=tmp;
            colSum[j]-=tmp;
        }
    }
        
    return mat;

【讨论】:

    猜你喜欢
    • 2021-04-03
    • 2015-12-23
    • 2011-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-25
    相关资源
    最近更新 更多