Parallel processing 给定一个N位数的基数3数字,生成所有基数3 N位数的数字,这些数字的k位数以并行方式不同

Parallel processing 给定一个N位数的基数3数字,生成所有基数3 N位数的数字,这些数字的k位数以并行方式不同,parallel-processing,combinatorics,Parallel Processing,Combinatorics,为了解决我的问题,使用基数为3的数字是很自然的。我有几个以3为基数的数字索引的表,在某个时候,我需要遍历所有以k位与给定N位数字不同的索引 例如,假设120为3位数的基数3,则1位数不同的数字为: 020 220 100 110 121 122 我有一些丑陋的代码,显然是这样做的,但是它很慢,很难并行化。你知道如何有效地做到这一点吗 (首选语言:c++)听起来您希望所有组合都有一位不同,而不是一位不同?1基3有2个不同于2基3的位(01对10) 如果这是您想要的,您可以通过拥有n个工作线程来并行

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

例如,假设120为3位数的基数3,则1位数不同的数字为: 020 220 100 110 121 122

我有一些丑陋的代码,显然是这样做的,但是它很慢,很难并行化。你知道如何有效地做到这一点吗


(首选语言:c++)

听起来您希望所有组合都有一位不同,而不是一位不同?1基3有2个不同于2基3的位(01对10)

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

这里是代码。有关各个命令的文档可以在中找到

对该代码的剖析:

  len = Max[{n, IntegerLength[num3]}];
  num = List /@ IntegerDigits[num3, 3, len];
假设您希望包含带前导零的数字,该函数将获取位数(n)作为参数。如果不这样做,如果数字有前导零,则将数字拆分为单个数字不会生成n个数字。第二行将类似2110的数字转换为列表{{2}、{1}、{1}、{0}
IntegerDigits
进行拆分,并将
List/@
映射到生成的数字上,放置我们稍后需要的额外花括号

    num3T = num;
    num3T[[ss]] = num3T[[ss]] /. {{0} -> {1, 2}, {1} -> {0, 2}, {2} -> {0, 1}}; 
这些子列表中的一些子列表将被替换(/。是替换运算符,替换所参与的部分由ss中的位置列表决定)替换为一组互补的基3位数字,以便命令
元组可以从它们中生成所有可能的集。例如
元组[{{1,2},{3},{4,5}]-==>{{1,3,4},{1,3,5},{2,3,4},{2,3,5}

    IntegerString[FromDigits[#], 10, len] & /@ Tuples[num3T],
元组
位于行的末尾。第一部分是一个纯函数,作用于
元组
函数的结果,用
fromdights
再次将其转换为数字,并使用
IntegerString
处理前导零(因此,结果是一个字符串,以允许前导零)

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

由于ss采取的每一组位置都会生成一个结果数字列表,因此最终结果是一个列表列表<代码>展平
将其展平为一个数字列表

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万组。如果我在
Table
中更改
ParallelTable
,需要3.463秒,大约慢5倍。有点令人惊讶,因为我只有4个内核,而且通常并行开销会消耗相当大的潜在速度增益。

你是对的,我的意思是这些位的值可以是0、1或2。然后是一个数字不同的组合。问题是,当你想改变k位数时,这并不容易,因为你有n/(k!(n-k)!)组合,用2^k乘以必须返回的数字总数。我需要一种方法来排序所有组合或类似的东西,这样我就可以编写一个简单的for循环,而不是许多嵌套的循环。非常感谢您的回复,看起来不错。我将尝试实现这一点@赫克托:祝你好运!代码< > Tuples < /C>和子集都是方便的,但我认为它们在C++中并不难实现。替换部分可能更难在C++中镜像。
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}