为给定的基数和位数生成所有可能的排列

时间:2012-04-23 08:47:48

标签: combinations loops

我确信这很简单,但我很难想办法做到这一点。基本上如果我有一个P柱和V ^ P行的数组,我怎样才能填写所有组合,即基本上是P数字基数V中所有可能的数字。例如,对于P = 3和V = 2:

000
001
010
011
100
101
110
111

请记住,这是一个二维数组,而不是一个整数数组。

P = 4且V = 3。

0000
0001
0002
0010
0011
0012
....

生成这个数组后,我正在尝试开发的其余工作是微不足道的。因此,非常感谢有关如何执行此操作的一些代码/提示。感谢。

4 个答案:

答案 0 :(得分:1)

基本上,这是从0到基数p中最大的数字宽度v的v p 个数字的列表。 numpy.base_repr可用于在Python中执行此操作:

from numpy import base_repr

def base_of_size(base, size):
    for i in range(base ** size):
        yield base_repr(i, base).rjust(size, "0")

另外,itertools.product(range(v), repeat=p)是另一个完成此任务的Python内置程序(结果效率最高-参见下面的基准)。

这是将numpy.base_repr中的算法转换为C#(Convert.ToString()对基数的选择非常严格):

using System;
using System.Collections.Generic;

class Converter
{
    public static IEnumerable<string> BaseOfSize(int baseN, int size) 
    {
        for (int i = 0; i < Math.Pow(baseN, size); i++) 
        {
              yield return BaseRepr(i, baseN).PadLeft(size, '0');
        }
    }

    public static string BaseRepr(int n, int baseN)
    {
        string digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        var res = new List<char>();

        for (int num = Math.Abs(n); num > 0; num /= baseN) 
        {
            res.Add(digits[num%baseN]);  
        }

        if (n < 0) res.Add('-');

        res.Reverse();
        return string.Join("", res);
    }

    public static void Main(string[] args) 
    {
        foreach (var n in BaseOfSize(2, 3)) 
        {
            Console.WriteLine(n);
        }

        Console.WriteLine();

        foreach (var n in BaseOfSize(3, 4)) 
        {
            Console.WriteLine(n);
        }
    }
}

输出:

000
001
010
011
100
101
110
111

0000
0001
0002
0010
0011
0012
0020
 ...
2220
2221
2222

尽管numpy版本易于使用且可迭代,但它也很慢。使用递归DFS方法意味着我们不必从头开始计算每个数字,而可以简单地递增之前的数字,直到到达新的叶子为止。这些版本不使用生成器,但这是一个简单的调整:

Python:

def base_of_size(base, size):
    def recurse(res, row, i=0):
        if i >= size:
            res.append(row[:])
        else:
            for j in range(base):
                row[i] = j
                recurse(res, row, i + 1)

        return res

    return recurse([], [None] * size)

C#:

using System;
using System.Collections.Generic;

class Converter
{
    public static List<List<int>> BaseOfSize(int v, int p) 
    {
        var res = new List<List<int>>();
        BaseOfSize(v, p, 0, new List<int>(new int[p]), res);
        return res;
    }

    private static void BaseOfSize(int v, int p, int i, List<int> row, List<List<int>> res)
    {
        if (i >= p) 
        {
            res.Add(new List<int>(row));
        }
        else 
        {
            for (int j = 0; j < v; j++) 
            { 
                row[i] = j;
                BaseOfSize(v, p, i + 1, row, res);
            }
        }
    }
}

快速基准测试(带有生成器):

from itertools import product
from time import time
from numpy import base_repr

def base_of_size(base, size):
    def recurse(res, row, i=0):
        if i >= size:
            yield row[:]
        else:
            for j in range(base):
                row[i] = j
                yield from recurse(res, row, i + 1)

        return res

    yield from recurse([], [None] * size)

def base_of_size2(base, size):
    for i in range(base ** size):
        yield base_repr(i, base).rjust(size, "0")

if __name__ == "__main__":
    start = time()
    list(base_of_size(10, 6))
    end = time()
    print("dfs:", end - start)
    start = time()
    list(base_of_size2(10, 6))
    end = time()
    print("base_repr:", end - start)
    start = time()
    list(product(range(10), repeat=6))
    end = time()
    print("product:", end - start)

输出:

dfs: 4.616123676300049
base_repr: 9.795292377471924
product: 0.5925478935241699

itertools.product以远射获胜。

答案 1 :(得分:0)

以P = 3和V = 2为例,在第一列中需要这个数字序列:

0, 0, 0, 0, 1, 1, 1, 1

所以你基本上想要四个0,然后是四个1。

在第二栏中,您需要:

0, 0, 1, 1, 0, 0, 1, 1

所以你想要两个0,然后是两个1,然后又一个。

通常,在列号n中,您需要每个数字的V ^(P-n),重复V ^(n-1)次。

当P = 3且V = 2时的示例:

第1列:我们需要每个数字的V ^(P-n)= 2 ^(3-1)= 4,重复V ^(n-1)= 2 ^ 0 = 1次:

[0, 0, 0, 0, 1, 1, 1, 1]

第2列:我们需要每个数字的V ^(P-n)= 2 ^(3-2)= 2,重复V ^(n-1)= 2 ^ 1 = 2次:

[0, 0, 1, 1], [0, 0, 1, 1]

第3列:我们需要每个数字的V ^(P-n)= 2 ^(3-3)= 1,重复V ^(n-1)= 2 ^ 2 = 4次:

[0, 1], [0, 1], [0, 1], [0, 1]

生成此序列的一些Python代码:

def sequence(v, p, column):
    subsequence = []
    for i in range(v):
        subsequence += [i] * v**(p - column)
    return subsequence * v**(column - 1)

答案 2 :(得分:0)

如果每个“数字”中有不同数量的选项,则可以使用此代码。

也许在某些优化工具中存在执行此操作的算法,因为它可能对蛮力方法很有用。下面的代码添加了一个列,显示“最大位数”,可以忽略:

import numpy as np
val=np.arange(15)
options=[2,2,3]
print(val)
print(options)
opt = options + [1] # Assumes options to be a list
opt_cp = np.flip(np.cumprod(np.flip(np.array(opt))))
ret = np.floor_divide(val[:,np.newaxis], opt_cp[np.newaxis,:])
ret[:,1:] = np.remainder(ret[:,1:], np.array(opt[:-1])[np.newaxis,:])
inds = ret[:,1:]
print(inds)

答案 3 :(得分:0)

您也可以使用 numpy 的 N 维网格函数。

例如

np.mgrid[0:2,0:2,0:2].reshape((3, 8)).T

array([[0, 0, 0],
       [0, 0, 1],
       [0, 1, 0],
       [0, 1, 1],
       [1, 0, 0],
       [1, 0, 1],
       [1, 1, 0],
       [1, 1, 1]])

np.stack(np.meshgrid(range(2), range(2), range(2), indexing='ij')).reshape(3, -1).T

或一般对于任何 PV

np.mgrid[[slice(0, V)]*P].reshape((P, -1)).T

np.stack(np.meshgrid(*[range(V)]*P, indexing='ij')).reshape((P, -1)).T

一定有更明显的方法,但我想不出是什么。