素数的组合产品


21

问题陈述

给定一组唯一的,连续的素数(不一定包括2个),则生成这些素数的第一幂的所有组合的乘积(例如,无重复)以及1。例如,给定的集{2,3,5, 7},您产生{1,2,3,5,6,7,10,14,15,21,30,35,42,70,105,210},因为:

  1  =  1
  2  =  2
  3  =  3
  5  =  5
  6  =  2 x 3
  7  =  7
 10  =  2 x 5
 14  =  2 x 7
 15  =  3 x 5
 21  =  3 x 7
 30  =  2 x 3 x 5
 35  =  5 x 7
 42  =  2 x 3 x 7
 70  =  2 x 5 x 7
105  =  3 x 5 x 7
210  =  2 x 3 x 5 x 7

请注意,如果输入集的基数为k,则将在输出集中给您2 ^ k个成员。

规则/条件

  1. 您可以使用任何语言。目的是使源代码的字符数最少。
  2. 您的解决方案必须是完整的程序或完整的功能。该函数可以是匿名的(如果您的语言支持匿名函数)。
  3. 您的解决方案应该能够支持至少2 ^ 31的产品。如果传递的乘积太大而无法表示,就不必担心检测或处理整数溢出。但是,请说明您的计算极限。
  4. 您可以接受列表或集合,也可以生成列表或集合。您可以假定输入已排序,但不需要产生已排序的输出。

背景

什么时候或为什么有用?一个非常有用的地方是生成一个乘数表,以一种称为方型分解的整数分解算法进行并行竞争。。在那里,您尝试的每个奇数乘子都会使算法在硬半素数上失败(找到一个因数)的可能性降低约50%。因此,对于生成素数{3,5,7,11}的集合(该集合生成一组16个试乘子以并行竞争),该算法在硬质半素数上大约失败2 ^ –16的时间。将13加到素数列表中,将产生一组32个试验乘数,将失败的机会降低到大约2 ^ –32,从而在不增加计算开销的情况下显着提高了结果(因为即使并行乘以两倍的乘数,平均而言,它仍然以相同的总步数找到答案)。

Answers:


18

纯Bash,32个字节

eval echo \$[{1,${1// /\}*{1,}}]

读取作为命令行参数传递的输入列表(以空格分隔)。

使用了三种不同的外壳扩展:

  1. ${1// /\}*{1,}参数扩展,在替代空间2 3 5 7}*{1,给予2}*{1,3}*{1,5}*{1,7\$[{1,}]分别添加到开始和结束以给出\$[{1,2}*{1,3}*{1,5}*{1,7}]\$[为避免在此阶段尝试进行算术扩展,将使用反斜杠转义。
  2. \$[{1,2}*{1,3}*{1,5}*{1,7}]大括号的扩展。因为括号扩展通常发生在参数扩展之前,所以我们不得不使用eval强制参数扩展首先发生的方式。大括号扩展的结果是$[1*1*1*1] $[1*1*1*7] $[1*1*5*1] ... $[2*3*5*7]
  3. $[1*1*1*1] $[1*1*1*7] $[1*1*5*1] ... $[2*3*5*7]算术扩展的列表,然后对其求值以给出我们所需的数字列表。

输出:

$ ./comboprime.sh "2 3 5 7"
1 7 5 35 3 21 15 105 2 14 10 70 6 42 30 210
$

3
头脑...吹...哇!
托德·雷曼

WTF ...我得到了1 0
username.ak

@ username.ak您输入什么?您如何输入它(命令行参数?)。您正在运行什么版本的bash?bash --version
Digital Trauma

12

CJam,13个字节

1aq~{1$f*+}/p

[2 3 5 7]从STDIN 读取数组(例如)。在线尝试。

匿名函数将具有相同的字节数:

{1a\{1$f*+}/}

运行示例

$ cjam <(echo '1aq~{1$f*+}/p') <<< '[]'
[1]
$ cjam <(echo '1aq~{1$f*+}/p') <<< '[2 3 5 7]'
[1 2 3 6 5 10 15 30 7 14 21 42 35 70 105 210]

怎么运行的

1a               " Push R := [1].              ";
  q~             " Read an array A from STDIN. ";
    {     }/     " For each a ∊ A:             ";
     1$f*+       "     R += { ra : r ∊ R }     ";
            p    " Print.                      ";

4
哇,这是遍历所有子集的聪明方法。
马丁·恩德

9

Haskell,22岁

解决方案是一个匿名函数:

map product.mapM(:[1])

用法示例:

*Main> map product.mapM(:[1]) $ [2,3,5]
[30,6,10,2,15,3,5,1]

说明:
(:[1])是给定数字的函数x返回list 的函数[x,1]
mapM(:[1])是一个给定数字列表的函数(:[1]),该函数将函数映射到它们上,并返回从每种列表中选择元素的所有可能方式。例如,mapM(:[1]) $ [3,4]首先将函数映射为get [[3,1] , [4,1]]。那么可能的选择是[3,4](选择两者的第一个数字)[3,1] [1,4][1,1]因此返回[[3,4],[3,1],[1,4],[1,1]]

然后map product映射所有选择并返回其产品,即所需的输出。

此函数的类型是多态的,意味着它可以对所有类型的数字进行运算。您可以输入的列表,Int结果将是的列表,Int但也可以应用于类型的列表Integer并返回的列表Integer。这意味着溢出行为不是由该函数指定的,而是由输入的类型指定的(是Haskell的表现型系统:))


真好!号码大小有限制吗?
Todd Lehman 2014年

1
@ToddLehman不。默认数字类型为Integer,这是一个无限的整数类型。还有Int一个32位整数,但这主要只是遗留的东西。
John Dvorak 2014年

@JanDvorak实际上是的,但是我太喜欢类型系统了,更不用说了:)。需要注意的另一件事是,由于它是匿名的,因此使用它很重要,因为在某些情况下可能会应用单态性限制。
骄傲的haskeller 2014年

8

Mathematica,18个 17字节

1##&@@@Subsets@#&

那是一个匿名函数。像这样称呼它

1##&@@@Subsets@#&[{2,3,5,7}]

马丁突然给出了一个简短的答案!
Todd Lehman

@ToddLehman现在让我们等待击败这个问题的J答案。;)
Martin Ender 2014年

1
如果Mathematica不是封闭源,则有人可以编写高尔夫球版本。×@@@𝒫@#应该是无与伦比的。
丹尼斯2014年

@Dennis Wolfram语言规范可独立于Mathematica使用,我认为有一个或两个(不完整)开源实现。曾有人建议创建一个Unicode别名的Mathematica版本,但我认为PPCG不会很好地接受它。^^
Martin Ender 2014年

2
@MartinBüttner抱歉,让您等待:(*/@#~2#:@i.@^#)J中的16个字符;)
algorithmhark

4

更新:C(函数f),92

即使是功能,这仍然是此处最长的条目。这是我第一次在C中将未知长度的数组作为函数参数传递,而且显然C函数无法知道传递给它的数组的长度,因为参数是作为指针传递的(不论使用哪种语法)。因此,需要第二个参数来指示长度。

我将输出保留为stdout,因为设置一个整数数组并返回它几乎肯定会更长。

感谢Dennis的提示。

请参阅f下面的测试程序中的功能(92个字符,不包括不必要的空格)。

通过printf输出

j;

f(int c,int*x){
  int p=1,i;
  for(i=c<<c;i--;p=i%c?p:!!printf("%d ",p))p*=(i/c>>i%c)&1?1:x[i%c];
}

main(int d,char**v){
  d--;
  int y[d];
  for(j=d;j--;)y[j]=atoi(v[j+1]);
  f(d,y);
}

通过数组指针输出

j,q[512];

f(int c,int*x,int*p){
    for(int i=-1;++i-(c<<c);p[i/c]*=(i/c>>i%c)&1?1:x[i%c])i%c||(p[i/c]=1);
}

main(int d,char**v){
  d--;
  int y[d];
  for(j=d;j--;)y[j]=atoi(v[j+1]);
  f(d,y,q);
  for(j=1<<d;j--;)printf("%d ",q[j]);
}

C(程序),108

排除不必要的空格。

p=1,i;
main(int c,char**v){
  c-=1;
  for(i=c<<c;i--;i%c||(printf("%d ",p),p=1))(i/c>>i%c)&1||(p*=atoi(v[i%c+1]));
}

从命令行输入,输出到标准输出。C不会在这里赢,但是明天我可能会尝试转换为函数。

基本上,我们遍历1<<c素数的所有组合,每一位i/c都与产品中是否存在特定素数相关。“内部循环” i%c遍历素数,并根据值乘以它们。i/c.i%c达到0时,将输出乘积,然后将其设置为1,以进行下一个“外部”迭代。

奇怪的是,printf("%d ",p,p=1)它不起作用(它总是打印1。)这不是我第一次看到一个奇怪的行为,当在a中使用一个值并在printf稍后将其分配到同一括号中时。在这种情况下,第二个逗号可能不会被视为参数分隔符,而是被视为运算符。

用法

$ ./a 2 3 5 7
1 2 3 6 5 10 15 30 7 14 21 42 35 70 105 210

C并没有严格定义参数求值的顺序。特别是,许多C函数调用的参数从右到左进行求值。
COTO 2014年

从第6.5.2.2 ISO / IEC 9899:TC3实际参数中的功能标志,实际参数的评估顺序,和子表达式是不确定的[。]所以,它是由在订购功能的编译器参数被评估。使用-Wsequence-point-Wall,GCC会抱怨。
丹尼斯

1.您可以更改c-=1c--甚至可以使用i=--c<<c,如果你不介意UB(它似乎工作,GCC)。2.的两种用法||都可以用三元运算符代替:p=i%c?p:!!printf("%d ",p)p*=(i/c>>i%c)&1?1:atoi(v[i%c+1])
Dennis

@丹尼斯感谢您的提示!我刚睡前发帖,所以程序刚开始运行。c-=1这种打高尔夫球是我不应该错过的,但这是一个快速的错误修复,因为我忘记了argv(程序名称)中还有一个额外的字符串,i=..c<<c可以在GCC / cygwin上使用,但是我离开了原来的按原样编程,然后继续执行功能。因此,我刚刚了解到sizeof在作为函数参数传递的数组上不起作用。我已经将您对三元运算符的建议合并到该函数中。我一直坚持将输出输出到stdout,因为我看不到返回数组的捷径。
级圣河

是的,作为函数参数传递的数组会衰减到指针。-在C语言中传递指针到应该包含结果作为函数参数的数组的情况并不罕见。这个问题说,你可以假设产品有2 ^ 31小,所以你可以通过大小512的数组
丹尼斯

3

Haskell,27个字节

这是@sudo的CJam答案的Haskell实现,它是一个匿名函数。它不会击败@proud haskeller的很棒的Haskell解决方案,但是无论如何我都会把它放在这里。

foldr((=<<)(++).map.(*))[1]

说明: foldr接受一个二进制函数,一个值和一个列表。然后,它使用函数的应用程序替换列表中的每个cons单元格,并使用值替换列表的末尾,如下所示:foldr f v [a,b,c] == f a (f b (f c v))。我们的值是一个包含的单元素列表1,二进制函数是f = (=<<)(++).map.(*)。现在,f需要花费数n,使一个功能(n*),通过乘法n,从它使一个功能,g = map(n*)即该功能适用于列表中的所有元素,以及饲料那个功能(=<<)(++)。在这里,(++)是串联函数,并且(=<<)monadic bind,在这种情况下,它接受(++)g,并给出一个接受列表的函数,适用g 复制到它的副本,然后将两者串联。

简而言之:以开头[1],对于n输入列表中的每个数字,获取当前列表的副本,将所有内容乘以n,并将其附加到当前列表中。


3

Python:55个字符

f=lambda l:l and[x*l[0]for x in f(l[1:])]+f(l[1:])or[1]

通过依次选择包含或排除每个数字来递归生成产品。


递归解决方案!凉!
Todd Lehman

我认为and如果以相反的方式写总和可以删除空格吗?
mathmandan '16

@mathmandan是的,谢谢。
xnor

3

PARI / GP,26个字节

v->divisors(factorback(v))

较长的版本包括

v->divisors(prod(i=1,#v,v[i]))

(30个字节)和

v->divisors(fold((x,y)->x*y,v))

(31个字节)。

注意,如果输入是分解矩阵而不是集合,则divisors单独使用可以节省18个字节。但是将集合转换为因式分解矩阵似乎需要超过18个字节。(我可以直接v->concat(Mat(v~),Mat(vectorv(#v,i,1)))乘以39字节v->factor(factorback(v)),也可以乘以24字节,然后重构为24字节,有人能做得更好吗?)


2

贤者– 36 34

如果我正确理解的话,本质上与MartinBüttner的解决方案相同。正如我在评论中提到的那样,我不妨将其发布为答案。

lambda A:map(prod,Combinations(A))

这是一个匿名函数,例如可以按如下方式调用:

(lambda A:map(prod,Combinations(A)))([2,3,5,7])

1
您可以通过使其成为匿名函数来剃除2个字节(问题允许)
骄傲的haskeller

2

J(20)

结果比我期望或预期的还要长。仍然:比haskel短!

*/@:^"1#:@i.@(2&^)@#

用法:

    f=:*/@:^"1#:@i.@(2&^)@#
    f 2 3 5 7
1 7 5 35 3 21 15 105 2 14 10 70 6 42 30 210

这适用于任何一组数字,而不仅仅是素数。另外,素数可以是无限大小的,只要数组具有后缀即可x2 3 5 7x


*/@#~2#:@i.@^#是14个字节的替代方案。
英里


1

R,56个字节

r=1;for(i in 1:length(s))r=c(r,apply(combn(s,i),2,prod))

我在这里考虑s是集合(和一个列表)。我相信它可以做得更短。我会明白的。


1

PHP,97字节

<?for(;$i++<array_product($a=$_GET[a]);){$t=$i;foreach($a as$d)$t%$d?:$t/=$d;if($t<2)echo"$i\n";}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.