给定N位数的基数3,生成具有可并行化方式不同的k个数字的所有基数3个N位数

时间:2011-09-09 14:19:51

标签: parallel-processing combinatorics

为了解决我的问题,使用3号基数是很自然的。我有几个以3号基数索引的表,在某些时候我需要遍历所有与给定的N位数字相差k个数字的索引。

例如,给定120作为3位数的基数3,1位数的数字将是: 020 220 100 110 121 122

我有一些丑陋的代码以明显的方式执行此操作,但它很慢并且难以并行化。知道如何有效地做到这一点吗?

(首选语言:c ++)

2 个答案:

答案 0 :(得分:0)

听起来你想要一个数字的所有组合,而不是一个? 1个基数3有2个不同于2个基数3的比特(01对10)。

如果这是你想要的,你可以通过n个工作线程来并行化,你的数字是n位数。为每个线程提供原始数字和要更改的数字的偏移量。线程应返回更改该数字的两个数字。所以在你的例子中,线程0将传入(“120”,0)并返回(“121”,“122”)。然后你的主线程接收所有这些新数字并将它们添加到列表中。

答案 1 :(得分:0)

以下是Mathematica中的代码。有关各个命令的文档可以在Mathematica documentation center

中找到
digitReplacements[num3_, n_, k_] :=
 Module[{len, num, num3T},
  len = Max[{n, IntegerLength[num3]}];
  num = List /@ IntegerDigits[num3, 3, len];
  Flatten[
   ParallelTable[
    num3T = num;
    num3T[[ss]] = num3T[[ss]] /. {{0} -> {1, 2}, {1} -> {0, 2}, {2} -> {0, 1}}; 
    IntegerString[FromDigits[#], 10, len] & /@ Tuples[num3T],
    {ss, Subsets[Range[len], {k}]}
    ], 1
   ]
  ]

解读此代码:

  len = Max[{n, IntegerLength[num3]}];
  num = List /@ IntegerDigits[num3, 3, len];

假设您要包含带前导零的数字,则函数将数字(n)作为参数。如果不这样做,如果数字的前导零,则在各个数字中分割数字将不会产生n位数。第二行将类似2110的数字转换为列表{{2},{1},{1},{0}}。 IntegerDigits对结果数字进行拆分并List /@映射List,放置我们稍后需要的额外花括号。

    num3T = num;
    num3T[[ss]] = num3T[[ss]] /. {{0} -> {1, 2}, {1} -> {0, 2}, {2} -> {0, 1}}; 

其中一些子列表将被替换的基数为3的数字替换(/。是替换运算符,替换运算符由ss中的位置列表确定),以便命令Tuples可以从他们那里制作所有可能的东西。例如Tuples[{{1,2},{3},{4,5}}]-==> {{1, 3, 4}, {1, 3, 5}, {2, 3, 4}, {2, 3, 5}}

    IntegerString[FromDigits[#], 10, len] & /@ Tuples[num3T],

Tuples位于该行的末尾。第一部分是纯函数,它对Tuples函数的结果起作用,使用FromDigits再次将其转换为数字,并使用IntegerString处理前导零(结果为因此,一个字符串,允许前导零。)

基于找到所有可能的替换位置,心脏就是这些元组表的生成。这是通过行Subsets[Range[len], {k}]完成的,它生成通过选择k个数字而生成的列表{1,2,...,n}的所有子集。 ParallelTable使用生成的位置在此列表上循环,以将这些位置的所有适用数字替换为可能的对应列表。生成此数字更改位置列表似乎是并行化问题的一种自然方法,因为您可以将列表的各个部分专用于各种核心。 ParallelTable是Mathematica标准Table函数的并行计算变体,它自动处理此并行化。

由于ss采取的每组位置都会生成结果数字列表,因此最终结果是列表列表。 Flatten将此缩小为一个数字列表。

digitReplacements[120, 3, 1]

==> {"010", "210", "100", "120", "111", "112"}

digitReplacements[2012, 5, 2]

==>{"10112", "11112", "20112", "21112", "12012", "12212", \
    "22012", "22212", "12102", "12122", "22102", "22122", "12110", \
    "12111", "22110", "22111", "00012", "00212", "01012", "01212", \
    "00102", "00122", "01102", "01122", "00110", "00111", "01110", \
    "01111", "02002", "02022", "02202", "02222", "02010", "02011", \
    "02210", "02211", "02100", "02101", "02120", "02121"}

digitReplacements[1220101012201010, 16, 6] // Length // Timing

==> {0.671, 512512}

因此,我们在0.671秒内发现了50万套。如果我在ParallelTable中更改Table,则需要3.463秒,这大约慢5倍。有点令人惊讶,因为我只有4个核心,并且通常并行开销会占用相当大一部分潜在的速度增益。