这是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}] 行完成的,该行生成列表{1,2,...,n} 的所有子集,这些子集通过选择k 个数字而生成。 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 个内核,而且通常并行开销会占用相当大一部分潜在的速度提升。