最快的可推算整数分解器


17

任务是找到一个复合数的重要因素。

在不超过140字节长的情况下,编写代码以尽可能快地找到复合数字的重要因素。输出应该只是您找到的因素。

您的代码可以采用任何方便的方式进行输入和输出,包括例如作为函数的参数。

列出所有因素的测试用例(您只需要输出一个)

187: 11 17
1679: 23 73
14369648346682547857: 1500450271 9576890767
34747575467581863011: 3628273133 9576890767
52634041113150420921061348357: 2860486313 5463458053 3367900313
82312263010898855308580978867: 264575131106459 311111111111113
205255454905325730631914319249: 2860486313 71755440315342536873 
1233457775854251160763811229216063007: 1110111110111 1000000000063 1111111999999
1751952685614616185916001760791655006749: 36413321723440003717 48112959837082048697

在以下困难的测试案例中,我不会给您打分,这可能是您感兴趣的测试:

513231721363284898797712130584280850383: 40206835204840513073 12764787846358441471

得分了

您的分数是将以上所有测试用例分解的总时间,每个分解失败的惩罚为10分钟(均四舍五入到最接近的秒数)。您的代码也应该适用于其他整数,这不应该只是硬编码这些答案。

10分钟后,我将停止您的代码。

如果两个人获得相同的分数,则第一个答案将获胜。

限制条件

您的代码不能使用任何执行整数分解的内置函数或库函数。您可以假设输入少于256位。所有输入数字将是复合的。

我将如何计时?

我实际上将time ./myprog在Ubuntu系统上运行以进行计时,因此也请提供一个完整的程序供我运行,其中包括您定义的任何功能。

编译语言说明

在我的机器上,编译时间不得超过1分钟。

真的有可能吗?

如果您忽略空间限制,则可以在我的计算机上使用纯Python代码+ pypy在不到2秒的时间内分解每个因素。

那么什么是非平凡的因子分解算法?

Pollard的rho算法快速且适合打高尔夫球。当然,还有许多其他方法可以分解整数

更快的是二次筛。将其压缩为140个字节似乎是一个严峻的挑战。

领先分数

  • SEJPM,最后一个测试用例将受到 10分钟的处罚,而Haskell中则需要 16秒

因此,我们可能会得到类似的数字2 ** 1024
科纳·奥布莱恩

@ ConorO'Brien您不会得到比测试用例更多的数字。

因此,就精度而言,不超过256位。
科纳·奥布莱恩

对于4之类的输入,输出应为2还是2, 2
Xcoder先生17年

1
@AndersKaseorg我根据您的建议更新了问题。谢谢。

Answers:


9

Haskell,100 97 91 89 87 72 67字节

在线尝试!

-3字节归功于@flawr
-6字节归功于@flawr
-2字节归功于@flawr再次
-2字节归功于一组优化的参数
-1字节归功于@flawrs又一次
归功于-14字节@AndersKaseorg 只需要输出一个因子
-5字节

f n|let s x=mod(x*x+7)n;a#b|d<-gcd(b-a)n,d>1=d|c<-s b=s a#s c=5#s 5

这在不明显的时间内可用于前5个测试用例。
在最大的测试用例上,这可能会超时。

通常,这通常会在时间上返回一个与最小因子的平方根成比例的非平凡因子。
它不会对每个输入都起作用,因为它不会改变多项式,并且很难在140个字节内检测到异常情况。
它也不会输出完整的因式分解,而是输出非平凡的因数以及输入除以该因数。
它还不会按大小对因素进行排序。

对于所有进一步的评估,使用的方法是Pollard-Rho-Factoring,标准起始值为2(x^2+1一次应用标准多项式),非标准多项式常数因子为7(因为1不适用于1679)。

完整程式(factor.hs):

import System.Environment(getArgs)

f n|let s x=mod(x*x+7)n;a#b|d<-gcd(b-a)n,d>1=d|c<-s b=s a#s c=5#s 5

main= do
      args <- getArgs
      print$f (read $ head args :: Integer)

编译为$ ghc factor.hs(需要ghc安装)。
运行为$ ./factor <number>

示例运行:

$ ./factor 187
11

取消程式码:

f n=g 5 (s 5)
   where s x=mod(x*x+7)n
         g a b = if d>1 then d else g(s a)(s(s b))
               where d=gcd(b-a)n

通过调用g初始值来计算非平凡因子。多项式在此处预先应用2,然后在结果(5)上重新应用,以便g(在“ where”子句中)的输入始终可以轻松用于gcd测试。g(高尔夫版本使用infix #),然后尝试计算一个非平凡的因子d(在非高尔夫版本的where子句中,在高尔夫版本中内联),作为的两个输入之间的差g,如果成功,则返回所述因子,否则重试。在这里,n如果a==b仅返回一个微不足道的因子,则它可能会作为输出生成,处理此问题的正确方法是在此事件发生时更改起始值或更改多项式。


|1<2=s a#(s$s b)可以用|c<-s b=s a#s c我认为的方式代替:)(另:为什么不发布TIO链接?)
更加模糊的

我根据评论建议更新了问题。现在,您只需要输出一个因子,并且可以保证数字是复合的。

3
PS:为什么我们打高尔夫球,这甚至都不是高尔夫运动
瑕疵的,2017年

4
您现在拥有53个字节,可以在其中实现更复杂的分解算法:)

1
您也可以取出abs ,因为b它总是非负数。(也许是您的意思abs$b-a,但gcd接受否定参数,并始终产生非否定的结果。)这使该结果降至不到一半的推文!
Anders Kaseorg '17

6

Pari / GP,137字节,〜5

使用GP的内置椭圆曲线操作(以及一些不足的参数调整)

ecm(n)=iferr(if(n%2==0,2,n%3==0,3,for(a=1,n,ellmul(ellinit(Mod([a,a^2-a-1],n)),[1,a],lcm([1..ceil(4^a^0.5)])))),e,gcd(n,lift(Vec(e)[3])))

ecm是(应该)返回一个因子的函数。在线尝试!

测试:

ecm(n)=iferr(if(n%2==0,2,n%3==0,3,for(a=1,n,ellmul(ellinit(Mod([a,a^2-a-1],n)),[1,a],lcm([1..ceil(4^a^0.5)])))),e,gcd(n,lift(Vec(e)[3])))

{
ns = [
  187,
  1679,
  14369648346682547857,
  34747575467581863011,
  52634041113150420921061348357,
  82312263010898855308580978867,
  205255454905325730631914319249,
  1233457775854251160763811229216063007,
  1751952685614616185916001760791655006749
  ]
}

test(n) = {
    d = ecm(n);
    if (!(1<d && d<n && n%d==0), error(d));
    print(n, ": ", d)
}

apply(test, ns)

quit

取消高尔夫:

ecm(n) = {
  iferr(if(n%2 == 0, 2,
           n%3 == 0, 3,
           for(a = 1, n,
               /* x^3 + A*x + B = y^2 */
               E = ellinit(Mod([a, a^2-a-1], n)); /* [A, B] */
               x0 = [1, a]; /* [x, y] */
               B = ceil(4^a^0.5); /* ~ exp(sqrt(log(p))), p ~= exp(a) */
               print("a=", a, ", B=", B);
               ellmul(E, x0, lcm([1..B]))
              )
          ),
         ERR, gcd(n, lift(Vec(ERR)[3] /* = Mod(d, n) */)),
         errname(ERR)=="e_INV")
}

可悲的是,处理因子2和3需要使用很多字节。可以用于添加阶段2的字节:

ecm(n)=iferr(for(a=1,n,Y=X=ellmul(E=ellinit(Mod([a,1],n)),[0,1],(B=ceil(4^a^0.5))!);for(z=0,9*B,Y=elladd(E,Y,X))),e,gcd(n,lift(Vec(e)[3])))

1

公理,137字节9分钟

p(n:PI):PI==(j:=1;a:=3;s:=n^.2;repeat(b:=j:=nextPrime(j);repeat(b<s=>(b:=b*j);break);a:=powmod(a,b,n);d:=gcd(a-1,n);d>1 or j>n=>break);d)

在函数p()之上,该函数将实现p-1算法以分解为文件中要复制的内容,以对p()函数进行测试

-- one has to copy this below text in a file name for example file.input
-- in one window where there is Axiom one could write 
-- )read C:\absolutepathwherethereisthatfile\file
-- and call the function test()
-- test()
-- the first character of all function and array must be afther a new line "\n"
)cl all
)time on
vA:=[187,1679,14369648346682547857,34747575467581863011,52634041113150420921061348357,82312263010898855308580978867,205255454905325730631914319249,1233457775854251160763811229216063007, 1751952685614616185916001760791655006749]

p(n:PI):PI==(j:=1;a:=3;s:=n^.2;repeat(b:=j:=nextPrime(j);repeat(b<s=>(b:=b*j);break);a:=powmod(a,b,n);d:=gcd(a-1,n);d>1 or j>n=>break);d)

-- this would try to factor n with p-1 Pollard method
pm1(n:PI):PI==
   j:=1;a:=3;s:=n^.2
   repeat
      b:=j:=nextPrime(j)
      repeat(b<s=>(b:=b*j);break)
      a:=powmod(a,b,n)
      d:=gcd(a-1,n);d>1 or j>n=>break
   d

test()==(for i in 1..#vA repeat output [vA.i, p(vA.i)])

结果在这里:

(5) -> test()
   [187,11]
   [1679,73]
   [14369648346682547857,9576890767]
   [34747575467581863011,9576890767]
   [52634041113150420921061348357,2860486313]
   [82312263010898855308580978867,311111111111113]
   [205255454905325730631914319249,2860486313]
   [1233457775854251160763811229216063007,1111111999999]
   [1751952685614616185916001760791655006749,36413321723440003717]
                                                               Type: Void
                              Time: 496.78 (EV) + 53.05 (GC) = 549.83 sec

您能从ubuntu的命令行中确切说明如何运行此代码吗?我已经安装了公理,并在其中创建了一个名为foo.ax的文件,其中包含您的非高尔夫代码。

@Lembik 1)在foo.input中重命名fop.ax 2)在一个终端或xterm中运行Axiom 3)在该Axiom终端中写入以下命令“)read C:absolutepath \ foo” 4)在Axiom的终端中写入调用功能test()。这是在Windows中执行的方法,在我看来这是打开一个Axiom会话并使用“)read”命令加载文件的线索
RosLuP

@Lembik如果文件有问题,我也认为也可以:1)运行Axiom 2)在Axiom程序中的<return>上写)时间3)复制粘贴并在Axiom程序的每个“复制粘贴”中按return键:数组vA,Axiom程序中的函数p()和test()4)写入test()<return>
RosLuP

@Lembik那么需要什么时间?
RosLuP

1

公理10分钟+ 31秒

A(a)==>a:=(a*a+7)rem n;z(n)==(p:=a:=b:=101;for i in 1..repeat(A(a);A(b);A(b);p:=mulmod(p,a-b,n);i rem 999<9=>(p:=gcd(p,n);p>1=>break));p)

z()是函数rho,一个137字节函数;解开z()并将其称为rho()。假设gcd(0,n)= n,则循环停止并返回失败n。

)time on    
rho(n)==
  p:=a:=b:=101
  for i in 1..repeat
          A(a);A(b);A(b)
          p:=mulmod(p,a-b,n)
          i rem 999<9=>(p:=gcd(p,n);p>1=>break)
  p

va1:=[187,1679,14369648346682547857,34747575467581863011,52634041113150420921061348357,82312263010898855308580978867,205255454905325730631914319249,1233457775854251160763811229216063007, 1751952685614616185916001760791655006749]
p1()==(for i in 1..#va1-1 repeat output [va1.i,z(va1.i)]) 

结果(z()可以接受,但最后一个数175195268561461618598591760791655006749仍未考虑在内(10分钟))

(6) -> p1()
   [187,17]
   [1679,23]
   [14369648346682547857,1500450271]
   [34747575467581863011,3628273133]
   [52634041113150420921061348357,2860486313]
   [82312263010898855308580978867,264575131106459]
   [205255454905325730631914319249,2860486313]
   [1233457775854251160763811229216063007,1111111999999]
                                                               Type: Void
                                 Time: 30.38 (EV) + 1.38 (GC) = 31.77 sec

(8) -> z(1679)
   (8)  23
                                                    Type: PositiveInteger
                                                              Time: 0 sec

0

Python 3中100个 99字节,45 40 39秒+ 10分钟罚

import math
def f(n):
 x=y=2;d=1
 while d<2:y=y*y+1;x,y=1+x*x%n,y*y%n+1;d=math.gcd(x-y,n)
 return d

在线尝试!

使用初始值2和多项式x ^ 2 + 1的Pollard-Rho。


您可以使用pow(带有第三个参数)提高执行速度。
mbomb007 '18
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.