Algorithm 每行和每列有两个非相邻非零的等概率随机平方二元矩阵的算法

Algorithm 每行和每列有两个非相邻非零的等概率随机平方二元矩阵的算法,algorithm,matrix,random,Algorithm,Matrix,Random,如果有人能给我指出一种算法,让我能够: 创建一个带有条目0和1的随机方阵,以便 每行和每列正好包含两个非零条目 两个非零条目不能相邻 所有可能的矩阵都是等概率的 现在,我通过以下步骤实现了第1点和第2点:这样一个矩阵可以通过适当的行和列排列,转换成一个具有该形式块的对角块矩阵 1 1 0 0 ... 0 0 1 1 0 ... 0 0 0 1 1 ... 0 ............. 1 0 0 0 ... 1 因此,我从这样一个矩阵开始,使用[0,…,n-1]的分区,通过随机排列行和列对其

如果有人能给我指出一种算法,让我能够:

  • 创建一个带有条目0和1的随机方阵,以便
  • 每行和每列正好包含两个非零条目
  • 两个非零条目不能相邻
  • 所有可能的矩阵都是等概率的
  • 现在,我通过以下步骤实现了第1点和第2点:这样一个矩阵可以通过适当的行和列排列,转换成一个具有该形式块的对角块矩阵

    1 1 0 0 ... 0
    0 1 1 0 ... 0
    0 0 1 1 ... 0
    .............
    1 0 0 0 ... 1
    
    因此,我从这样一个矩阵开始,使用[0,…,n-1]的分区,通过随机排列行和列对其进行置乱。不幸的是,我找不到整合邻接条件的方法,而且我很确定我的算法不会平等对待所有矩阵

    更新

    我已设法达到第3点。答案其实就在我眼前:我正在创建的块矩阵包含了考虑邻接条件所需的所有信息。首先是一些属性和定义:

    • 一个合适的矩阵定义了
      [1,…,n]
      的排列,可以这样构建:在
      1
      行中选择一个1。包含此条目的列在与1不同的行
      a
      上正好包含一个等于1的其他条目。同样,行
      a
      包含列中的另一个条目1,该列包含行
      b
      上的第二个条目1,依此类推。这将启动一个排列
      1->a->b…
    例如,使用以下矩阵,从标记的条目开始

    v
    1 0 1 0 0 0 | 1
    0 1 0 0 0 1 | 2
    1 0 0 1 0 0 | 3
    0 0 1 0 1 0 | 4
    0 0 0 1 0 1 | 5
    0 1 0 0 1 0 | 6
    ------------+--
    1 2 3 4 5 6 |
    
    我们得到排列
    1->3->5->2->6->4->1

    • 这种置换的循环导致了我前面提到的块矩阵。我还提到在行和列上使用任意排列对块矩阵进行置乱,以重建符合要求的矩阵
    但我使用的是任何排列,这会导致一些相邻的非零条目。为了避免这种情况,我必须选择将块矩阵中相邻的行(和列)分开的排列。事实上,更精确地说,如果两行属于同一个块并且是循环连续的(一个块的第一行和最后一行也被认为是连续的),那么我要应用的排列必须将这些行移动到最终矩阵的非连续行中(在这种情况下,我将称为两行不兼容)

    所以问题变成了:如何构建所有这样的排列

    最简单的想法是通过随机添加与前一行兼容的行来逐步构建置换。作为例子,考虑用例<代码> n=6 < /代码>使用分区<代码> 6=3+3 < /代码>和相应的块矩阵

    1 1 0 0 0 0 | 1
    0 1 1 0 0 0 | 2
    1 0 1 0 0 0 | 3
    0 0 0 1 1 0 | 4
    0 0 0 0 1 1 | 5
    0 0 0 1 0 1 | 6
    ------------+--
    1 2 3 4 5 6 |
    
    这里的行
    1
    2
    3
    互不兼容,正如
    4
    5
    6
    一样。随机选择一行,例如
    3

    我们将以数组的形式编写一个排列:
    [2,5,6,4,3,1]
    意思是
    1->2
    2->5
    3->6
    。。。这意味着块矩阵的行
    2
    将成为最终矩阵的第一行,行
    5
    将成为第二行,依此类推

    现在,让我们通过随机选择一行来构建一个合适的排列,比如说
    3

    • p=[3,…]
    然后从与
    3
    兼容的剩余行中随机选择下一行:
    4
    5
    6
    。假设我们选择
    4

    • p=[3,4,…]
    必须在
    1
    2
    之间进行下一个选择,例如
    1

    • p=[3,4,1,…]
    依此类推:
    p=[3,4,1,5,2,6]

    将此置换应用于块矩阵,我们得到:

    1 0 1 0 0 0 | 3
    0 0 0 1 1 0 | 4
    1 1 0 0 0 0 | 1
    0 0 0 0 1 1 | 5
    0 1 1 0 0 0 | 2
    0 0 0 1 0 1 | 6
    ------------+--
    1 2 3 4 5 6 |
    
    这样,我们就能够垂直隔离所有非零条目。对列也必须这样做,例如使用排列
    p'=[6,3,5,1,4,2]
    来最终获得

    0 1 0 1 0 0 | 3
    0 0 1 0 1 0 | 4
    0 0 0 1 0 1 | 1
    1 0 1 0 0 0 | 5
    0 1 0 0 0 1 | 2
    1 0 0 0 1 0 | 6
    ------------+--
    6 3 5 1 4 2 | 
    
    因此,这似乎非常有效,但构建这些排列需要谨慎,因为人们很容易陷入困境:例如,使用
    n=6
    和分区
    6=2+2+2
    ,遵循前面设置的构造规则可能会导致
    p=[1,3,2,4,
    。不幸的是,
    5
    6
    是不兼容的,因此选择其中一个会使最后一个选择变得不可能。我想我已经找到了所有导致死胡同的情况。我将用
    r
    表示剩余选项集:

  • p=[…,x,,]
    r={y}
    x
    y
    不兼容
  • p=[…,x,,?]
    r={y,z}
    ,其中
    y
    z
    都与
    x
    不兼容(无法选择)
  • p=[…,?,?]
    r={x,y}
    x
    y
    不兼容(任何选择都会导致情况1)
  • p=[…,?,?,?]
    r={x,y,z}
    x
    y
    z
    循环连续(选择
    x
    z
    将导致情况2,选择
    y
    进入情况3)
  • p=[…,w,,?,?]
    r={x,y,z}
    ,其中
    xwy
    为3个循环(既不能选择
    x
    也不能选择
    y
    ,选择
    z
    将导致情况3)
  • p=[…,?,?,?,?,?]
    r={w,x,y,z}
    ,其中
    wxyz
    为4个循环(任何选择都会导致情况4)
  • p=[…,?,?,,?,?]
    r={w,x,y,z}
    wi
    3  0
    4  2
    5  16
    6  722
    7  33988
    8  2215764
    9  179431924
    10 17849077140
    
    ------------
    no sol found
    ------------
    ------------
    no sol found
    ------------
    ------------
    no sol found
    ------------
    ------------
    sol not unique
    ------------
    ------------
    FOUND
    
    a -> !b
    <->
    !a v !b (propositional logic)
    
    sum(var_set) <= k
    <->
    sum(negated(var_set)) >= len(var_set) - k
    
    public static void main(String[] args) throws Exception {
        int n = 100;
        Random rnd = new Random();
        byte[] mat = new byte[n*n];
        byte[] colCount = new byte[n];
    
        //generate row by row
        for (int x = 0; x < n; x++) {               
    
            //generate a random first bit
            int b1 = rnd.nextInt(n);
            while ( (x > 0 && mat[(x-1)*n + b1] == 1) ||    //not adjacent to the one above
                    (colCount[b1] == 2)                     //not in a column which has 2
                    ) b1 = rnd.nextInt(n);
    
    
            //generate a second bit, not equal to the first one
            int b2 = rnd.nextInt(n);
            while ( (b2 == b1) ||                           //not the same as bit 1
                    (x > 0 && mat[(x-1)*n + b2] == 1) ||    //not adjacent to the one above
                    (colCount[b2] == 2) ||                  //not in a column which has 2
                    (b2 == b1 - 1) ||                       //not adjacent to b1
                    (b2 == b1 + 1)
                    ) b2 = rnd.nextInt(n);
    
    
            //fill the matrix values and increment column counts
            mat[x*n + b1] = 1;
            mat[x*n + b2] = 1;              
            colCount[b1]++;
            colCount[b2]++;
        }
    
        String arr = Arrays.toString(mat).substring(1, n*n*3 - 1);
        System.out.println(arr.replaceAll("(.{" + n*3 + "})", "$1\n"));     
    }
    
    package rndsqmatrix;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.stream.IntStream;
    
    public class RndSqMatrix {
    
        /**
         * Generate a random matrix
         * @param size the size of the matrix
         * @return the matrix encoded in 1d array i=(x+y*size)
         */
        public static int[] generate(final int size) {
            return generate(size, new int[size * size], new int[size],
                    new int[size]);
        }
    
        /**
         * Build a matrix recursivly with a random walk 
         * @param size the size of the matrix
         * @param matrix the matrix encoded in 1d array i=(x+y*size)
         * @param rowSum
         * @param colSum
         * @return 
         */
        private static int[] generate(final int size, final int[] matrix,
                final int[] rowSum, final int[] colSum) {
    
            // generate list of valid positions
            final List<Integer> positions = new ArrayList();
            for (int y = 0; y < size; y++) {
                if (rowSum[y] < 2) {
                    for (int x = 0; x < size; x++) {
                        if (colSum[x] < 2) {
                            final int p = x + y * size;
                            if (matrix[p] == 0
                                    && (x == 0 || matrix[p - 1] == 0)
                                    && (x == size - 1 || matrix[p + 1] == 0)
                                    && (y == 0 || matrix[p - size] == 0)
                                    && (y == size - 1 || matrix[p + size] == 0)) {
                                positions.add(p);
                            }
                        }
                    }
                }
            }
    
            // no valid positions ?
            if (positions.isEmpty()) {
    
                // if the matrix is incomplete => return null
                for (int i = 0; i < size; i++) {
                    if (rowSum[i] != 2 || colSum[i] != 2) {
                        return null;
                    }
                }
    
                // the matrix is complete => return it
                return matrix;
            }
    
            // random walk 
            Collections.shuffle(positions);
            for (int p : positions) {
                // set '1' and continue recursivly the exploration
                matrix[p] = 1;
                rowSum[p / size]++;
                colSum[p % size]++;
                final int[] solMatrix = generate(size, matrix, rowSum, colSum);
                if (solMatrix != null) {
                    return solMatrix;
                }
    
                // rollback 
                matrix[p] = 0;
                rowSum[p / size]--;
                colSum[p % size]--;
            }
    
            // we can't find a valid matrix from here => return null
            return null;
        }
    
        public static void printMatrix(final int size, final int[] matrix) {
            for (int y = 0; y < size; y++) {
                for (int x = 0; x < size; x++) {
                    System.out.print(matrix[x + y * size]);
                    System.out.print(" ");
                }
                System.out.println();
            }
        }
    
        public static void printStatistics(final int size, final int count) {
            final int sumMatrix[] = new int[size * size];
            for (int i = 0; i < count; i++) {
                final int[] matrix = generate(size);
                for (int j = 0; j < sumMatrix.length; j++) {
                    sumMatrix[j] += matrix[j];
                }
            }
            printMatrix(size, sumMatrix);
        }
    
        public static void checkAlgorithm() {
            final int size = 8; 
            final int count = 2215764;
            final int divisor = 122;
            final int sumMatrix[] = new int[size * size];
            for (int i = 0; i < count/divisor ; i++) {
                final int[] matrix = generate(size);
                for (int j = 0; j < sumMatrix.length; j++) {
                    sumMatrix[j] += matrix[j];
                }
            }
            int total = 0;
            for(int i=0; i < sumMatrix.length; i++) {
                total += sumMatrix[i];
            }
            final double factor = (double)total / (count/divisor);
            System.out.println("Factor=" + factor + " (theory=16.0)");
        }
    
        public static void benchmark(final int size, final int count,
                final boolean parallel) {
            final long begin = System.currentTimeMillis();
            if (!parallel) {
                for (int i = 0; i < count; i++) {
                    generate(size);
                }
            } else {
                IntStream.range(0, count).parallel().forEach(i -> generate(size));
            }
            final long end = System.currentTimeMillis();
            System.out.println("rate="
                    + (double) (end - begin) / count + "ms/matrix");
        }
    
        public static void main(String[] args) {
            checkAlgorithm();
            benchmark(8, 10000, true);
            //printStatistics(8, 2215764/36);
            printStatistics(8, 2215764);
        }
    }
    
    Factor=16.0 (theory=16.0)
    rate=0.2835ms/matrix
    552969 554643 552895 554632 555680 552753 554567 553389 
    554071 554847 553441 553315 553425 553883 554485 554061 
    554272 552633 555130 553699 553604 554298 553864 554028 
    554118 554299 553565 552986 553786 554473 553530 554771 
    554474 553604 554473 554231 553617 553556 553581 553992 
    554960 554572 552861 552732 553782 554039 553921 554661 
    553578 553253 555721 554235 554107 553676 553776 553182 
    553086 553677 553442 555698 553527 554850 553804 553444