残数系统


26

就大量挑战而言,我认为这可能很有趣。

在此挑战中,我们将使用残数系统(RNS)对大整数执行加法,减法和乘法。

什么是RNS

RNS是人们开发用于识别整数的多种方法之一。在该系统中,数字由一系列残差表示(它们是模运算后的结果(即整数除法后的余数))。在此系统中,每个整数都有许多表示形式。为了使事情简单,我们将限制事情,以便每个整数都唯一表示。我认为用一个具体的例子来描述正在发生的事情比较容易。

让我们看一下前三个质数:2、3、5。在RNS系统中,我们可以使用这三个数来唯一表示任何小于2 * 3 * 5 = 30的残数。采取21:

21小于30,因此我们可以使用将2、3和5修改后的结果来表示它(即,整数除以2、3和5之后的余数)

我们将使用以下整数序列来标识21:

21〜{21 mod 2,21 mod 3,21 mod 5} = {1,0,1}

因此,在我们的RNS系统中,我们将使用{1,0,1}而不是“ 21”。

通常给定整数n,我们将n表示为{ n mod 2,...,n mod p_k },其中p_k是最小素数,因此n小于等于或等于p_k的所有素数的乘积。

另一个例子,假设我们有3412。在这里我们需要使用2,3,5,7,11,13,因为2*3*5*7*11*13=300302*3*5*7*11=2310这太小了。

3412〜{3412 mod 2,3412 mod 3,3412,mod 5,...,3412 mod 13} = {0,1,2,3,2,6}

您会注意到,使用此系统,我们可以相对轻松地表示非常大的数字。使用{1,2,3,4,5,6,7,8,...}残基,我们可以表示最多{2,6,30,210,2310,30030,510510,9699690 ...}分别。(这是系列

我们的任务

我们将使用这些残基对大数进行+,-和*。我将在下面描述这些过程。现在,这里是输入和输出规格。

输入项

您将通过stdin或function参数获得两个(可能非常大)的数字。它们将以基数为10的字符串给出。

为了进一步概述问题,我们将第一个输入n称为第二个输入m假设n> m> = 0

您还将获得+-*指示要执行的操作。

输出量

x为整数。我们将使用[ X ]指以上描述的RNS表示X

你要输出 [n] <operator> [m] = [result]

如何在RNS中执行操作

这些操作相对简单。给定RNS表示法中的两个数字,要对其进行加,减或乘运算,只需简单地按分量执行给定的运算,然后取模。

{1,2,3} + {1,1,4} = {(1 + 1)mod 2,(2 + 1)mod 3,(3 + 4)mod 5} = {0,0,2}

请注意,如果用于表示两个不同数字的残基数不相同,则在执行操作时,您将需要扩展“较短”数,以使其具有相同的残基数。这遵循相同的过程。有关示例,请参见测试用例。

如果结果需要比任何一个输入更多的残基,情况也是如此。然后,两个输入都需要“扩展”。

重要细节

  • 我们将在这里处理大数字,但不会任意大。我们将负责直至前100个质数乘积的数字(请参见下文)。为此,您将免费获得前100个素数(无字节费用)。您可以将它们粘贴在称为p或您的语言习惯的数组中,然后从最终总数中减去用于启动此数组的字节数。当然,这意味着它们可能是硬编码的,或者您可以使用内置函数来生成它们。

  • 如果由于某种原因,这是您的语言中使用的默认整数表示形式。那样就好。

  • 除非它是您的语言的默认设置,否则您不能使用任何Arbitrary Precision Integer类型。如果是默认值,则不能使用它存储通常不适合64位的整数。

  • 需要明确的是,每个整数将始终以尽可能少的残基表示。这适用于输入和输出。

  • 我认为其他规范应该避免这种情况,但是要避免多余:您可能不对输入执行给定的操作,然后再将所有内容更改为RNS,然后输出。您必须将输入更改为RNS,然后执行操作以产生输出。

测试用例

  1. 输入:

n = 10
m = 4
+

输出:

{ 0, 1, 0 } + { 0, 1 } = { 0, 2, 4 }

说明:

首先,如上所述,将每个数字更改为其RNS表示形式:

10 ~ {0,1,0}4 ~ {0,1}。请注意,当我们要进行按分量添加时,10具有比更多的组件4。因此,我们必须“扩展”较短的数字。因此,我们将简要介绍4 ~ {0,1} --> {0,1, 4 mod 5} = {0,1,4}。现在我们进行加法运算,然后取模数。

  1. 输入项
n=28
m=18
+

输出:

 [ 0, 1, 3 ] + [0, 0, 3 ] = [ 0, 1, 1, 4 ]
  1. 输入(我在键盘上捣蛋)
n=1231725471982371298419823012819231982571923
m=1288488183
*

输出(为了便于阅读,分行显示):

[1, 2, 3, 6, 2, 10, 2, 1, 12, 16, 7, 15, 34, 29, 31, 5, 55, 32, 66, 61, 3, 76, 52, 14, 65, 44, 99, 57 ] 
* 
[1, 0, 3, 3, 4, 8, 9, 10, 8, 0 ] 
= 
[1, 0, 4, 4, 8, 2, 1, 10, 4, 0, 17, 7, 27, 21, 44, 51, 56, 9, 6, 9, 12, 0, 52, 36, 43, 68, 99, 24, 96, 39, 96, 66, 125] 

n需要28个素数。m需要10。n*m需要33。

  1. 输入项
n=8709668761379269784034173446876636639594408083936553641753483991897255703964943107588335040121154680170867105541177741204814011615930342030904704147856733048115934632145172739949220591246493529224396454328521288726490
m=1699412683745170450115957274739962577420086093042490863793456500767137147999161679589295549397604032154933975242548831536518655879433595016
-

输出:

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 509]
-
[0, 2, 1, 6, 1, 12, 11, 18, 14, 28, 21, 36, 37, 42, 16, 52, 41, 60, 16, 70, 49, 78, 80, 88, 49, 100, 13, 106, 4, 112, 68, 130, 36, 138, 37, 150, 0, 162, 8, 172, 163, 180, 18, 192, 129, 198, 135, 222, 78, 228, 90, 238, 57, 250, 36, 262, 87, 270, 206, 280, 193, 292, 253, 310, 224, 316, 57, 336, 48, 348]
=
[0, 1, 4, 1, 10, 1, 6, 1, 9, 1, 10, 1, 4, 1, 31, 1, 18, 1, 51, 1, 24, 1, 3, 1, 48, 1, 90, 1, 105, 1, 59, 1, 101, 1, 112, 1, 0, 1, 159, 1, 16, 1, 173, 1, 68, 1, 76, 1, 149, 1, 143, 1, 184, 1, 221, 1, 182, 1, 71, 1, 90, 1, 54, 1, 89, 1, 274, 1, 299, 1, 266, 1, 228, 1, 340, 1, 170, 1, 107, 1, 340, 1, 88, 1, 157, 1, 143, 1, 22, 1, 22, 1, 58, 1, 296, 1, 371, 1, 140]

n使用100个素数。m使用70个素数。n-m使用99个素数。

我使用ChineseRemGAP中的中国余数定理的内置实现检查了它们(基本上采用RNS数字并将其更改为以10为基数的整数)。我相信他们是正确的。如果有些东西看起来可疑,请告诉我。


对于那些关心的人,前100个素数的乘积是:

471193079990618495316248783476026042202057477340967552018863483961641533584503
422120528925670554468197243910409777715799180438028421831503871944494399049257
9030720635990538452312528339864352999310398481791730017201031090

这个数字比使用给定系统可以表示的最大数字大1(和100个质数限制)。

有点相关


我认为执行操作绝不是最困难的部分,对此我感到很奇怪。
njpipeorgan

@njpipeorgan我同意,(a,b,o)=>a.map((v,i)=>eval(v+o+b[i]))例如,仅在ES6中执行该操作。我认为最困难的部分可能是在不使用任意精度算术的情况下,找到表示结果所需的素数,尽管随后转换为RNS并不是一件容易的事。
尼尔

我可以输入像这样的(1234,1234,+)吗?
clismique

@derpfacePython yes函数也可以接受
Liam

“仅按组件执行给定的操作” -那么输出中的额外组件从何而来?
smls

Answers:


6

间隙

背景知识:我会承认,在几个月前提出这个问题时,我还没有一种方法可以解决这个问题的难处:确定要使用的质数的正确数目。这个网站上有很多非常聪明的人,我真的希望有人会找到一种相当快的方法。但是,由于这没有发生,因此我甚至不确定是否确实有可能解决此问题。因此,我不得不花时间设计一种方法。我相信我所做的一切不会违反这项挑战中的任何规则,当然,我希望对此进行事实检查。

我也对选择感到遗憾,因为解决方案的深度比通常适合标记格式的深度略多。话虽如此,为遵循站点规则,本文底部有我解决方案的“ golf”版本。


### The first 100 primes;
primes := Primes{[1..100]};

### In many of the functions below, the 'string' variable is a string of digits
###


### Returns the 'index' digit of 'string' as an integer
GetValueAsInt := function(string, index) 
    return IntChar(string[index]) - 48;
end;

### Used in the 'modulus' function. See that comment for more information. 
### Calculates the contribution to the modulus of a digit 'digit' in the 10^power place.
### 'integer' is the modulus
digit_contribution := function(digit, integer, power)
    local result, i;
    result := 1;
    for i in [0..power-1] do
        result := ( result * (10 mod integer) ) mod integer;
    od;
    result := (result * (digit mod integer) ) mod integer;
    return result;
end;

### This modulus function is used to calculate the modulus of large numbers without storing them
##### as large numbers.
### It does so by breaking them into digits, and calculating the contribution of each digit.
### e.g. 1234 mod 5 = (1000 mod 5)(1 mod 5) + (200 mod 5)(2 mod 5) + (10 mod 5)(3 mod 5) + (4 mod 5)
### It actually mods after every calculation to ensure that we never get a number larger
##### than the modulus ('integer') squared, which will never be even close to 10^64-1
modulus := function(string, integer)
    local i, result, digit, len;
    len := Length(string);
    result := 0;
    for i in [1..len] do
        digit :=  IntChar(string[i]) -48;
        result := ( result + digit_contribution(digit, integer, len-i) )  mod integer;
    od;
    return result;
end;

### This returns the product of the first i-1 primes (mod j). It must not (and does not)
##### ever store an integer larger than 2^64-1
phi_i := function(i,j)
    local index, result;
    result := 1;
    for index in [1..i-1] do
        result := ( result * primes[index] ) mod primes[j];
    od;
    return result;
end;

### Calculates the first residues of 'string' mod the first 100 primes
get_residues := function(string) 
    local p, result;
    result := [];
    for p in primes do
        Add( result, modulus(string, p) );  
    od; 
    return result;
end;

### Gets the ith element in the partial_chinese array, given the previous elements
### See the explanation section and partial_chinese function for more info
get_partial_i := function( i, residues, previous_array )
    local index, result;
    result := residues[i];
    for index in [1..Length(previous_array)] do
        result := ( result - previous_array[index]*phi_i(index,i) ) mod primes[i]; 
    od;     
    result := ( result / phi_i(i,i) ) mod primes[i];
    return result;
end;

### returns an array such that the sum of prod_primes(i)*array[i] is equal to the integer value
##### that is represented by the residues. (It basically just does the CRT without
##### actually summing everything.) prod_primes(i) is the product of the first i-1 primes 
### See the explanation for a bit more info
### This is what allows us to determine the minimal number of primes to represent a RNS number
partial_chinese := function( string )
    local array, i, residues;
    residues := get_residues(string);
    array := [];        
    for i in [1 .. Length(primes)] do
        Add( array, get_partial_i( i, residues, array ) );
    od;
    return array;   
end;

### Same as partial_chinese but takes input in a different form.
partial_chinese_from_residues := function(residues)
    local array, i;
    array := [];        
    for i in [1 .. Length(primes)] do
        Add( array, get_partial_i( i, residues, array ) );
    od;
    return array;
end;

### gives you the number of primes needed to represent an integer. Basically asks how 
##### many trailing zeros there are in the chinese array.
get_size := function(string)
    local array, i, len, result;
    array := partial_chinese(string);
    len := Length(array);
    for i in [0..len-1] do
        if  not (array[len-i] = 0) then
            return len -i;
        fi; 
    od; 
    Print("ERROR: get_size().\n");
    return 0;
end;

### Same as above but with different input format
get_size_from_residues := function(residues)
    local array, i, len, result;
    array := partial_chinese_from_residues(residues);
    len := Length(array);
    for i in [0..len-1] do
        if  not (array[len-i] = 0) then
            return len -i;
        fi; 
    od; 
    Print("ERROR: get_size().\n");
    return 0;
end;

### the actual function. inputs are all strings
f := function(in1, in2, opperation)
    local residues_1, residues_2, residues_result, i;
    residues_1 := get_residues(in1);
    residues_2 := get_residues(in2);
    residues_result := [];
    if opperation = "+" then
        for i in [1..Length(primes)] do
            Add( residues_result, ( residues_1[i] + residues_2[i] ) mod primes[i]);
        od;     
    elif opperation = "*" then
        for i in [1..Length(primes)] do
            Add( residues_result, ( residues_1[i] * residues_2[i] ) mod primes[i]);
        od;     
    elif opperation = "-" then
        for i in [1..Length(primes)] do
            Add( residues_result, ( residues_1[i] - residues_2[i] ) mod primes[i]);
        od;     
    fi;
    Print(residues_1{[1..get_size(in1)]}, " ", opperation, " ", residues_2{[1..get_size(in2)]}, " = ", residues_result{[1..get_size_from_residues(residues_result)]} );
end;

说明:

首先,我们计算两个输入的所有100个残基。我们使用modulus代码中的函数执行此操作。我尝试要小心,以便我们mod在每一步之后都使用内置函数。这样可以确保我们永远不会有大于的数字,该数字540^2比第100个素数平方小1。

拥有所有残差后,我们可以执行给定的操作并mod再次执行每个条目。现在,我们为结果指定了一个唯一的标识符,但是我们需要确定代表结果和每个输入的最小条目数。

到目前为止,弄清我们实际上需要多少残留物是最困难的部分。为了确定这一点,我们执行了中国剩余定理(CRT)的大多数步骤。但是,显然我们必须进行修改,以免最终数字不会太大。

prod(i)第一个i-1素数之和。例如,

prod(1) = 1
prod(2) = 2
prod(3) = 6
prod(4) = 30
etc

X是一个整数。让{r_i}是的残基X,即

r_i = X mod p_i

哪里p_ii个素数。这是针对1<i<=100我们的情况。

现在,我们将使用CRT找到一个序列{u_i}使得在总和iprod(i) * u_i等于X。请注意,从u_i技术上讲,每个也是残基,例如u_i < p_i。此外,如果X < prod(i)那么u_i = 0。这是至关重要的。这意味着通过检查尾随零,我们可以确定r_i实际需要X在RNS中表示多少个残基。

如果您希望检查的某些序列u_i,该partial_chinese函数将返回该u_i序列。

通过弄乱CRT,我能够找到这些u_i值的递归公式,从而解决了确定需要多少残基的问题。

公式为:

u_i = [ r_i - SUM ] / prod(i)       (mod p_i)

SUM超过总和j in [1,i)u_j * prod(i)

当然,prod(i)在某些情况下,由于它太大而无法实际计算。为此,我使用了该phi_i功能。此函数返回prod(j) (mod p_i)。它mod步步为营,因此我们永远不会真正计算太大的东西。

如果您好奇该公式的来源,我建议您使用几个CRT示例,可以在Wikipedia页面上找到它们。

最后,对于每个输入以及我们的输出,我们计算u_i序列,然后确定尾随零。然后我们r_i从残基序列的末尾扔掉那么多。


“ Golfed”代码,2621字节

primes:=Primes{[1..100]};GetValueAsInt:=function(string,index)return IntChar(string[index])-48;end;digit_contribution := function(digit, integer, power)local result, i;result:=1;for i in [0..power-1] do result := ( result * (10 mod integer) ) mod integer;od;result:=(result*(digit mod integer) ) mod integer;return result;end;modulus:=function(string, integer)local i,result,digit,len;len:=Length(string);result:=0;for i in [1..len] do digit:= IntChar(string[i])-48;result:=(result+digit_contribution(digit,integer,len-i)) mod integer;od;return result;end;phi_i:=function(i,j)local index,result;result:=1;for index in [1..i-1] do result:=(result*primes[index] ) mod primes[j];od;return result;end;get_residues:=function(string) local p,result;result:=[];for p in primes do Add(result,modulus(string,p));od;return result;end;get_partial_i:=function(i,residues,previous_array)local index,result;result:=residues[i];for index in [1..Length(previous_array)] do result:=(result-previous_array[index]*phi_i(index,i) ) mod primes[i];od;result:=(result/phi_i(i,i)) mod primes[i];return result;end;partial_chinese:=function(string)local array,i,residues;residues:=get_residues(string);array:=[];for i in [1 .. Length(primes)] do Add(array,get_partial_i(i,residues,array));od;return array;end;partial_chinese_from_residues:=function(residues)local array,i;array:=[];for i in [1..Length(primes)] do Add(array,get_partial_i(i,residues,array));od;return array;end;get_size:=function(string)local array,i,len,result;array:=partial_chinese(string);len:=Length(array);for i in [0..len-1] do if not (array[len-i] = 0) then return len-i;fi;od;Print("ERROR: get_size().\n");return 0;end;get_size_from_residues:=function(residues)local array,i,len,result;array:=partial_chinese_from_residues(residues);len:=Length(array);for i in [0..len-1] do if not (array[len-i]=0) then return len-i;fi;od;Print("ERROR: get_size().\n");return 0;end;f:=function(in1,in2,opperation)local residues_1,residues_2,residues_result,i;residues_1:=get_residues(in1);residues_2:=get_residues(in2);residues_result:=[];if opperation = "+" then for i in [1..Length(primes)] do Add(residues_result,(residues_1[i]+residues_2[i] ) mod primes[i]);od;elif opperation = "*" then for i in [1..Length(primes)] do Add(residues_result,(residues_1[i]*residues_2[i])mod primes[i]);od;elif opperation = "-" then for i in [1..Length(primes)] do Add(residues_result,(residues_1[i]-residues_2[i]) mod primes[i]);od;fi;Print(residues_1{[1..get_size(in1)]}, " ", opperation, " ", residues_2{[1..get_size(in2)]}, " = ", residues_result{[1..get_size_from_residues(residues_result)]} );end;

我很困惑,因为常规RNS不会根据需要更改维度,但是您是否不通过从输入中计算扩展的100个残差数而不是仅计算所需的维度然后执行操作来弯曲规则?“ 对我来说,“首先,将每个数字更改为如上所述的 RNS表示形式 ”对我来说意味着'RNS'数字在完成任何操作之前应该只具有所需的残基。
Linus

抱歉@Linus,我以为我已经对此做出了回应。我同意您的看法,但是我认为所需的更改(我将进行)相对来说是微不足道的。正如我所看到的,我要做的就是在执行操作之前计算输入的残差长度。对所有三个数字使用全部100个质数只是利用了一个事实,即所有数字都在上述范围内
Liam

@Linus,回答第一个问题时,通常所有数字都会使用相同数量的残基。这会使问题变得简单得多
-Liam

2

Mathematica,不打高尔夫球

rns[d_,l_]:=Table[Reap[
    FoldPairList[Sow@QuotientRemainder[10#+#2,Prime@i]&,0,d]
  ][[2,1,-1,2]],{i,l}];
plus[a_,b_]:=Mod[a+b,Prime@Range@Length@a];
subtract[a_,b_]:=Mod[a-b,Prime@Range@Length@a];
times[a_,b_]:=Mod[a b,Prime@Range@Length@a];
mag[f_]:=LengthWhile[FoldList[#/#2&,f,Prime@Range@100],#>1.1&];
ext[m_,n_,i_]:=Fold[Mod[1##,Prime@i]&,m,Prime@Range@n];
multi[e_,p_,t_]:=Tr@Position[Mod[e Range@p,p],p-t];
appx[d_] := N@FromDigits[{d~Take~UpTo[6], Length@d}]
  • 函数rns[d_,l_]将以10 d为底的整数转换为length的RNS整数l

  • 功能plus/ times/ subtract添加/乘法/减法一个RNS整数从另一个/,两者是相同的长度。

  • 函数mag[f_]根据f其RNS表示的长度的下限来估计浮点数的近似大小。

  • 功能ext[m_,n_,i_]找出从产品的余数mPrime[Range@n]通过Prime[i]

  • 函数multi[e_,p_,t_]给出m满足的最小乘数Divisible[m*e+t,p]

  • 函数appx[d_]采用6十进制整数的第一位数字,并给出其近似浮点值。


借助上述功能,现在我们能够解决一个棘手的问题- 确定结果的长度

首先,我必须澄清一下,确定整数的RNS长度并非易事。对于小整数,我们可以将它们直接与素数的乘积进行比较。但是对于非常大的整数,由于禁止无限精确地计算素数的乘积,因此这种比较不再有效。

例如,假设素数1为的乘积303.16*10^46,则RNS周围整数的长度3.16*10^46可能为2930。该函数mag将为29这些整数提供参考,表明2930都是可能的。

一旦知道了幅度,我们就可以直接根据该幅度简单地表示整数,希望计算其真实长度。这里的窍门是在原始数字上添加一些新数字并修改其RNS表示,直到表示全为零。

例如,mag[211.]is 4,其长度4表示为{1, 1, 1, 1}

step 1:   {1,1,1,1} -> {0,2,2,2}  by adding  (1) * 1 = 1
step 2:   {0,2,2,2} -> {0,0,1,6}  by adding  (2) * 2 = 4
step 3:   {0,0,1,6} -> {0,0,0,2}  by adding  (2*3) * 4 = 24
step 4:   {0,0,0,2} -> {0,0,0,0}  by adding  (2*3*5) * 6 = 180
step 5:   calculate 211 + (1 + 4 + 24 + 180) ~ 420

通过增加一些数字,我们增加到211可被2102*3*5*7)整除的最小数字。现在我们得出结论,原始数大于210,因为420等于“约”两倍210。不难想象,如果我们从头开始209,最终数字是“大约” 210

函数length[f_,n_]采用浮点值f来估计幅度,并根据其RNS表示对其进行校正n

length[f_,n_]:=With[{g=mag@f},
    g+If[#==0,1,Round[(#+f)/Times@@Prime@Range@g]-1]&[
      FoldList[Times,1.,Prime[Range[g-1]]].
      FoldPairList[
        Block[{i=#2,m},
          {m=multi[ext[1,i-1,i],Prime@i,Part@##],rnsPlus[#,ext[m,i-1,#]&/@Range[g]]}
        ]&,n,Range[g]]]]

函数rnsOperation[a_,b_,op_,rnsop_]给出rnsop[a,b]并且op是对应的常规操作,用于基于浮点值获得近似结果。

rnsOperation[a_,b_,op_,rnsop_]:=Block[{c=op[appx@a,appx@b],m},
    m=mag[c];m=length[c,rnsop[rns[a,m],rns[b,m]]];rnsop[rns[a,m],rns[b,m]]]

rnsOperation[
    IntegerDigits@1231725471982371298419823012819231982571923,
    IntegerDigits@1288488183,
    Times, times]
(* {1,0,4,4,8,2,1,10,4,0,17,7,27,21,44,51,56,9,6,9,12,0,52,36,43,68,99,24,96,39,96,66,125} *)

1
不幸的是,我们帮助中心中概述的规则要求所有提交内容必须严格符合所使用的获胜标准。对于标准高尔夫比赛,这意味着所有提交的东西都必须打高尔夫球。
丹尼斯

@丹尼斯我知道这条规则。但是,即使没有打高尔夫球,我也认为这个问题既困难又复杂,因此解决这个问题而不是打高尔夫球是我的目标。
njpipeorgan

与我的java programm:P相比,这也许不算什么,但是该死的短了,尽管我的programm可能要快得多。
HopefulHelpful

1
我觉得您有能力打高尔夫
Rohan Jhunjhunwala

2

Python 3,435字节

这个挑战已经存在了一段时间,但是直到最近才出现:a)我投入时间和精力来尝试答案。b)实际检验了我的想法,以使用对数和中国余数定理的不正确组合来计算数字的大小(以及通过将其与基数的大小进行比较来计算素数的数量)。不幸的是,在尝试确定例如large_primorial + 3所需的素数的同时使用对数意味着我不得不找到解决浮点数问题的方法。

因此,这就是利亚姆的答案

在线尝试!

from functools import reduce as R
G=range
d=lambda s:[R(lambda z,c:(z*10+int(c))%q,s,0)for q in p]
h=lambda j,i:R(lambda z,q:z*q%p[i],p[:j],1)
def s(r):
 a=[];z=99
 for i in G(100):
  P=p[i];u=r[i]
  for j in G(len(a)):u=(u-a[j]*h(j,i))%P
  for k in G(1,P):
   if h(i,i)*k%P<2:break
  a+=u*k%P,
 while(a[z]<1)*z:z-=1
 return r[:z+1]
def f(a,b,n):u=d(a);v=d(b);print(s(u),n,s(v),'=',s([eval(str(u[i])+n+str(v[i]))%p[i]for i in G(100)]))

说明

在尝试传达Liam的答案时,我个人发现给出的某些解释措词混乱,因此这是我试图解释其算法的尝试。

首先,我们得到的残留物nm

res1 = get_residues(n)
res2 = get_residues(m)

这涉及将输入字符串的所有数字都转换为模数,然后将它们转换为每个素数的模数,例如对于28 [(20 + 8) mod 2, (20 + 8) mod 3, (20 + 8) mod 5, etc]

def get_residues(string):
    result = []
    for p in primes:
        result.append(reduce(lambda z, c:(z*10+int(c)) % p, string, 0))

然后我们使用成对的残基成对加,乘或减去 eval()

result = []
for i in range(len(primes)):
    result.append((eval(str(res1[i]) + op + str(res2[i])) % primes[i])

然后我们得到残基的大小,即我们需要的最小质数。

size1 = get_size(res1)
size2 = get_size(res2)
size3 = get_size(result)

获取大小是最棘手且最耗代码的部分。我们使用该partial_chinese函数,该函数使我们获得u_i确定大小的顺序。稍后再继续u_i

def get_size(residues):
    array = partial_chinese(residues)
    size = len(residues)-1
    while array[size] == 0 and size:
        size -= 1
    return size+1  # to prevent off-by-one errors from 0-indexing

u_i通过取每个残基r_i,减去总和u_j * primorial(j) for j in [1, i),然后dividing乘以primorial(i)所有模,来计算序列primes[i]。即u_i = (r_i - SUM) / primorial(i)。稍后将详细介绍我们的原始和除法功能。

def partial_chinese(residues):
    array = []
    for i in range(len(primes)):
        array.append(get_partial_i(i, residues, array))
    return array

def get_partial_i(i, residues, previous_array):
    result = residues[i]
    for j in range(len(previous_array)):
        result = (result - previous_array[j] * phi_i(j, i)) % primes[i]
    result = result * inverse(phi_i(i, i), primes[i]) % primes[i]
    return result

phi_i(j, i)计算primorial(j) mod primes[i]。司模任何素p很容易通过手工检查乘法逆实现,我们可以肯定,任何可能的u_i0 <= u_i < p保证是质数p和这样保证乘法逆。

def phi_i(j, i):
    return reduce(lambda z, q: z * q % primes[i], primes[:j], 1)

def inverse(n, p):
    for i in range(1, p):
        if n * i % p == 1:
            return i

完成所有这些之后,我们就打印了字符串,我们就完成了。

print(res1[:size1], op, res2[:size2], "=", result[:size3])

下一步是什么

实施起来很有趣。我仍然想看看是否可以在其他答案中以某种方式使用对数。而且我想实现此代码或诸如APL或Jelly之类的功能性高尔夫语言的代码。欢迎任何和所有高尔夫建议和更正!

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.