Kalıntı Sayı Sistemi


26

Çok sayıdaki zorluğun damarı içerisinde bunun ilginç olabileceğini düşündüm.

Bu zorlukta, büyük tamsayılarda toplama, çıkarma ve çarpma işlemleri yapmak için Artık Sayı Sistemini (RNS) kullanacağız.

RNS nedir

RNS, insanların tam sayıları tanımlamak için geliştirdikleri birçok yoldan biridir. Bu sistemde, sayılar bir kalıntı dizisi ile temsil edilir (ki bu bir modül işleminden sonraki sonuçlardır (yani, tamsayı bölünmesinden sonra kalan)). Bu sistemde, her bir tamsayı birçok gösterime sahiptir. Her şeyi basit tutmak için, her bir tamsayı benzersiz bir şekilde temsil edilmek üzere işleri sınırlayacağız. Somut bir örnekle neler olduğunu anlatmanın daha kolay olduğunu düşünüyorum.

İlk üç asal sayıya bakalım: 2, 3, 5. RNS sisteminde, bu üç sayıyı, artıkları kullanarak 2 * 3 * 5 = 30'dan küçük herhangi bir sayıyı benzersiz şekilde göstermek için kullanabiliriz . 21'i al.

21, 30'dan küçüktür, bu yüzden 2, 3 ve 5 ile modlama sonrası sonuçları kullanarak temsil edebiliriz (yani, tamsayıdan sonra 2, 3 ve 5'e bölerek kalanı)

21'i aşağıdaki tamsayılar dizisiyle tanımlayacağız:

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

Ve böylece RNS sistemimizde "21" yerine {1,0,1} kullanırdık.

Genelde n bir tamsayı verilirse , n'yi { n mod 2, ..., n mod p_k } olarak temsil ederiz , burada p_k en küçük asaldır, öyle ki n , p p'den küçük veya eşit olan tüm primerlerin ürününden daha azdır .

Başka bir örnek, biz 3412. söylüyorlar nedeniyle burada 2,3,5,7,11,13 kullanmak gerekmez 2*3*5*7*11*13=30030, oysa 2*3*5*7*11=2310çok küçük olduğu.

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

Bu sistemi kullanarak çok büyük sayıları nispeten acısız bir şekilde temsil edebileceğimizi fark ediyorsunuz. {1, 2, 3, 4, 5, 6, 7, 8, ...} kalıntılarını kullanarak, {2, 6, 30, 210, 2310, 30030, 510510, 9699690 ...} 'e kadar sayıları temsil edebiliriz. sırasıyla. ( İşte dizi )

Görevimiz

Bu kalıntıları çok sayıda üzerinde +, - ve * yapmak için kullanacağız. Bu süreçleri aşağıda tarif edeceğim. Şimdilik burada giriş ve çıkış bilgileri verilmiştir.

Giriş

Bir stdin veya işlev argümanı ile size iki (potansiyel olarak çok büyük) sayı verilir. 10 basamak baz dizileri olarak verilecektir.

Ayrıca aksaklıkları belirten amaçları doğrultusunda, ilk girişi diyoruz nve ikinci m. Varsayalım n> m> = 0 .

Ayrıca verilecek +ya -ya *gerçekleştirmek için operasyon belirtmek için.

Çıktı

Let x bir tamsayı. Yukarıda x'in yukarıda açıklanan RNS temsiline atıfta bulunmak için [ x ] kullanacağız .

Çıkacaksın [n] <operator> [m] = [result]

RNS'de işlemler nasıl gerçekleştirilir

Bu işlemler nispeten basittir. RNS notasyonunda iki sayı verilmişse, toplama, çıkarma veya çarpma işlemlerinde, verilen işlemleri tek tek uygulayın ve ardından modülü alın.

yani

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

İki farklı sayıyı temsil etmek için kullanılan kalıntıların sayısı aynı değilse, işlemler gerçekleştirilirken, aynı sayıda kalıntıya sahip olması için "daha kısa" sayıyı uzatmanız gerekeceğini unutmayın. Bu aynı işlemi takip eder. Örnek için test durumlarına bakın.

Aynı sonuç, her iki girişten daha fazla artık gerektiriyorsa geçerlidir. Ardından her iki girişin de "uzatılması" gerekir.

Önemli Detaylar

  • Burada büyük sayılarla uğraşacağız, fakat keyfi olarak büyük değil. İlk 100 astarın ürün sayısına kadar olan rakamlardan biz sorumlu olacağız (aşağıya bakınız). Bu amaçla, ilk 100 astarı ücretsiz olarak (bayt ücreti yok) verilir . Bunları, pkendi dilinize deyimsel olarak adlandırılmış bir diziye ya da aptalca bir şeye yapıştırabilir ve daha sonra bu diziyi başlatmak için kullanılan bayt sayısını son toplamınızdan çıkarabilirsiniz. Elbette bu, kodlanmış olmaları veya bunları oluşturmak için yerleşik kullanmaları anlamına gelir.

  • Birisi için bu, dilinizde kullanılan varsayılan tamsayı gösterimidir. Bu iyi.

  • Dilinizin varsayılanı olmadığı sürece herhangi bir Rasgele Hassas Tamsayı türü kullanamazsınız. Varsayılan ise, 64 bit'e sığmayan tam sayıları depolamak için kullanamazsınız.

  • Açık olmak gerekirse, her bir tam sayı her zaman mümkün olan en az kalıntı ile temsil edilecektir. Bu hem giriş hem de çıkış için geçerlidir.

  • Diğer özelliklerin bunu engellemesi gerektiğini düşünüyorum, ancak fazlalık olması gerekmiyor: Girdilerde verilen işlemi yapamayabilir ve ardından her şeyi RNS ve sonra çıktı olarak değiştirebilirsiniz. Girişleri RNS olarak değiştirmeli ve ardından çıkışı üretmek için işlemleri yapmalısınız.

Test Kılıfları

  1. Giriş:

n = 10
m = 4
+

Çıktı:

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

Açıklama:

İlk olarak, her sayıyı yukarıda açıklandığı gibi RNS gösterimi ile değiştirin:

10 ~ {0,1,0}ve 4 ~ {0,1}. Bileşen düzeyinde ekleme yapmak istediğimizde, bundan 10daha fazla bileşene sahip olduğuna dikkat edin 4. Bu nedenle kısa sayıyı "uzatmak" zorundayız. Böylece kısaca yazacağız 4 ~ {0,1} --> {0,1, 4 mod 5} = {0,1,4}. Şimdi eklemeye devam ediyoruz ve sonra modülü alıyoruz.

  1. Giriş
n=28
m=18
+

Çıktı:

 [ 0, 1, 3 ] + [0, 0, 3 ] = [ 0, 1, 1, 4 ]
  1. Giriş (klavyede yüzümü karıştırıyorum)
n=1231725471982371298419823012819231982571923
m=1288488183
*

Çıktı (okunabilirlik için ayrı satırlara bölünür):

[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] 

n28 astar gerektirir. m10. n*mgerektirir 33 gerektirir.

  1. Giriş
n=8709668761379269784034173446876636639594408083936553641753483991897255703964943107588335040121154680170867105541177741204814011615930342030904704147856733048115934632145172739949220591246493529224396454328521288726490
m=1699412683745170450115957274739962577420086093042490863793456500767137147999161679589295549397604032154933975242548831536518655879433595016
-

Çıktı:

[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]

n100 astar kullanır. m70 astar kullanır. n-m99 astar kullanır.

Bunları ChineseRem, GAP üzerine Çin Kalan Teoreminin yerleşik uygulamasını kullanarak kontrol ettim (temelde RNS sayılarını alır ve bunları 10 tamsayıya dönüştürür). Doğru olduklarına inanıyorum. Bir şey balık gibi görünüyorsa, lütfen bana bildirin.


Önemseyenler için ilk 100 astarın ürünü:

471193079990618495316248783476026042202057477340967552018863483961641533584503
422120528925670554468197243910409777715799180438028421831503871944494399049257
9030720635990538452312528339864352999310398481791730017201031090

Bu sayı, verilen sistemi kullanarak temsil edebileceğimiz maksimum sayıdan 1'dir (ve 100 asal sınırlama).

Biraz ilgili


Operasyonu gerçekleştirmenin, bu zorluk konusunda kendimi garip hissettiğim en zor kısım olmaktan uzak olduğunu düşünüyorum.
njpipeorgan

@ njpipeorgan Kabul ediyorum, (a,b,o)=>a.map((v,i)=>eval(v+o+b[i]))işlemin yapılması örneğin ES6'da. Bence en zor kısım, rasgele hassas aritmetik kullanmadan sonucu temsil etmek için gereken primerlerin sayısını bulmak olduğunu düşünüyorum.
Neil

Böyle bir girişi alabilir miyim ( 1234,1234,+)?
clismique,

@derpfacePython evet fonksiyonları da kabul edilebilir
Liam

"basitçe verilen işlemleri bileşen olarak gerçekleştirin" - peki çıktıdaki ilave bileşenler nereden geliyor?
sms

Yanıtlar:


6

GAP

Bazı Geçmiş: Bu soruyu oluşturduğumda, aylar önce, bu sorunun zor kısmını çözmek için bir yönteme sahip olmadığımı itiraf edeceğim: kullanılacak doğru asal sayının belirlenmesi. Bu sitede bir sürü zeki insan var ve gerçekten de birinin bunu oldukça hızlı bir şekilde yapmanın bir yolunu bulmasını bekliyordum. Ancak, bu olmadığından, bu sorunu çözmenin gerçekten mümkün olduğundan bile emin değildim. Bu yüzden, bir yöntem geliştirmek için zaman ayırmam gerekti. Yaptığım şeyin bu meydan okumadaki kuralların hiçbirini bozmadığına inanıyorum, elbette bunu kontrol etmeyi çok isterim.

Ayrıca seçiminden biraz pişmanım çünkü çözümler genellikle etiket biçimine göre biraz daha derin. Bunu söyledikten sonra, site kurallarına uymak için, bu yazının altında çözümümün "golf" bir sürümü var.


kod

### 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;

Açıklama:

Başlamak için her iki giriş için de tüm 100 kalıntıları hesaplanır. Bunu moduluskoddaki işlevle yapıyoruz . modHer adımdan sonra yerleşik işlevi kullanmak için dikkatli olmaya çalıştım . Bu, asla 100'den büyük 540^2asal kareden 1 küçük olandan daha büyük bir sayıya sahip olmamamızı sağlar.

Tüm kalıntılara sahip olduktan sonra, verilen işlemi ve modher girişi tekrar yapabiliriz. Şimdi sonuç için benzersiz bir göstergeye sahibiz, ancak sonucu ve girişlerin her birini temsil etmek için kullanmamız gereken minimum giriş sayısını belirlememiz gerekir.

Gerçekte ihtiyacımız olan kaç tortuyu bulmak, bu problemin en zor kısmı. Bunu belirlemek için, Çin Kalan Teoremi'nin (CRT) adımlarının çoğunu gerçekleştiriyoruz. Ancak, çok büyük sayılarla bitmeyecek şekilde değişiklikler yapmamız gerektiği açıktır.

prod(i)İlk i-1astarların toplamı olsun . Örneğin,

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

XBir tamsayı olalım . {r_i}Kalıntıları olsun X, yani

r_i = X mod p_i

Nerede p_iolduğunu iinci asal. Bu 1<i<=100bizim durumumuz için.

Şimdi bir diziyi bulmak için CRT kullanacağız {u_i}üzerinde toplamı öyle ki ibir prod(i) * u_ieşittir X. Her u_ibirinin ayrıca teknik olarak bir artık olduğunu unutmayın u_i < p_i. Ayrıca, eğer X < prod(i)öyleyse u_i = 0. Bu kritik öneme sahiptir. Bu, takip eden sıfırları inceleyerek, gerçekte RNS'de kaç tane r_itemsil etmemiz gerektiğini belirleyebileceğimiz anlamına gelir X.

Bazı dizileri incelemeyi düşünüyorsanız u_i, partial_chineseişlev u_idiziyi döndürür .

CRT ile uğraşarak, u_ideğerler için özyinelemeli bir formül bulabildim , ne kadar tortuya ihtiyacımız olduğunu belirleme sorununu çözdüm.

Formül:

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

Nerede SUMüzerinde toplamıdır j in [1,i)arasında u_j * prod(i).

Elbette prod(i)bazı durumlarda hesaplanamıyor çünkü çok büyük. Bu amaçla phi_iişlevi kullandım . Bu işlev döner prod(j) (mod p_i). O modyüzden biz çok büyük aslında hesapla bir şey asla, her adımda bu.

Bu formülün nereden geldiğini merak ediyorsanız, wikipedia sayfasında bulunabilecek birkaç CRT örneği çalışmanızı tavsiye ederim .

Son olarak, çıktılarımızın yanı sıra her girdi için, u_idiziyi hesaplar ve sonra takip eden sıfırları belirleriz. Sonra r_iartık kalıntı dizilerinin sonundan o kadarını atıyoruz .


"Golf" Kodu, 2621 bayt

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;

Kafam karıştı, çünkü normal RNS, boyutları gerektiği gibi değiştirmiyor, ancak uzatılmış 100 kalıntı sayısını girdiden hesaplayarak, yalnızca gereken boyutlardan ziyade, ardından işlemleri gerçekleştirerek kuralları bükemiyor musunuz? "İlk önce, her bir numarayı yukarıda açıklandığı gibi RNS temsiline göre değiştir " bana göre, 'RNS' numarasının, herhangi bir şey yapılmadan önce sadece gerekli kalıntılara sahip olması gerektiği anlamına gelir.
Linus

Üzgünüm @Linus, buna çoktan cevap verdiğimi düşündüm. Size katılıyorum, ancak gerekli olan değişikliğin (yapacağım) göreceli olarak önemsiz olduğunu düşünüyorum. Gördüğüm gibi, tek yapmam gereken işlemi gerçekleştirmeden önce girdilerin kalıntı uzunluklarını hesaplamak. Her 100 sayı için 100 primin tümünü kullanmak, yalnızca tüm sayıların yukarıda sınırlandırıldığı gerçeğinden yararlanır
Liam

@Linus ve ilk sorunuza cevap olarak, normalde tüm sayılar aynı sayıda kalıntı kullanır. Bu, soruyu çok daha basit hale getirirdi
Liam

2

Mathematica, golf oynamak değil

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}]
  • İşlev rns[d_,l_], bir baz-10 dtamsayısını, uzunluktaki bir RNS tamsayısına dönüştürür l.

  • plus/ times/ subtractAdd / multiply / işlevi , her ikisi de aynı uzunlukta olan bir RNS tamsayısını diğerinden / diğerinden çıkar.

  • İşlev mag[f_], kayan nokta sayısının yaklaşık büyüklüğünü f, RNS gösteriminin uzunluğunun alt sınırına göre tahmin eder.

  • Fonksiyon ext[m_,n_,i_]ürününün bölünmesinden kalan bulur mve Prime[Range@n]ile Prime[i].

  • Fonksiyon multi[e_,p_,t_]en küçük çarpanını verir mo tatminDivisible[m*e+t,p]

  • İşlev appx[d_], 6ondalık bir tamsayıdaki ilk haneleri alır ve yaklaşık kayan nokta değerini verir.


Yukarıdaki fonksiyonların yardımıyla, şimdi zor bir problemi çözebiliriz - sonucun uzunluğunu belirlemek için .

Öncelikle, bir tamsayının RNS uzunluğunu belirlemenin kolay bir iş olmadığını açıklığa kavuşturmak zorundayım. Küçük tamsayılar için bunları doğrudan primerlerin ürünü ile karşılaştırabiliriz. Fakat çok büyük tamsayılar için, primerlerin ürününü sınırsız bir şekilde hesaplamak yasak olduğundan, bu karşılaştırma artık işe yaramaz.

Örneğin, asal ürünü göz önüne alındığında 1To 30olduğu 3.16*10^46, etrafında tamsayılar RNS uzunluğu 3.16*10^46belki olabilir 29veya 30. İşlev magverecek 29hem de gösteren, bu tamsayılar için bir referans olarak 29ve 30mümkündür.

Büyüklüğü öğrendikten sonra, gerçek uzunluğunu hesaplamayı umarak doğrudan bu büyüklüğe göre tamsayıyı temsil ediyoruz. Buradaki hile, orijinal numaraya bazı yeni sayılar eklemek ve gösteriminin tümü sıfır olana kadar RNS gösterimini değiştirmek.

Örneğin, mag[211.]bir 4ve uzunluğu 4temsilidir {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

Bir sayı ekleyerek ( ) 211ile bölünebilen en küçük sayıya yükseliriz . Ve şimdi orjinal sayının daha büyük olduğu sonucuna varıyoruz , çünkü "yaklaşık" değerinin iki katıdır . Başlarsak , son sayının "yaklaşık" olduğunu hayal etmek zor değil .2102*3*5*7210420210209210

İşlev , büyüklüğü tahmin etmek length[f_,n_]için kayan nokta değerini alır fve RNS gösterimini temel alarak düzeltin 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]]]]

İşlev , kayan nokta değerlerine dayanarak yaklaşık sonuç almak için kullanılan normal işlemleri rnsOperation[a_,b_,op_,rnsop_]verir rnsop[a,b]ve opkarşılık gelir.

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]]]

Örnek

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
Ne yazık ki, yardım merkezimizde belirtilen kurallar , tüm gönderimlerin kullanımdaki kazanma kriterleri için ciddi bir rakip olmasını gerektiriyor. Bir kod golf yarışması için, bu, tüm bildirimlerin golf ile atılması gerektiği anlamına gelir.
Dennis,

@Dennis Bu kuralı biliyorum. Ancak, golf oynamadan bile, bu sorunun yeterince zor ve karmaşık olduğunu düşünüyorum, bu yüzden golf oynamak yerine bu sorunu çözmek benim amacım.
njpipeorgan

Bu belki de golf değil ama java programıma göre çok kısa: P, programım muhtemelen daha hızlı olmasına rağmen.
Umarım

1
Bunu golf yapabiliyor gibi hissediyorum
Rohan Jhunjhunwala

2

Python 3 , 435 bayt

Bu zorluk bir süredir kova listemde oldu, ancak son zamanlarda: a) Aslında bir cevap vermeye çalışıyorum; ve b) aslında benim saygılar (ve dolayısıyla ilkellerin büyüklüğüyle karşılaştırarak asal sayılar) boyutunu hesaplamak için benim fikrimi test etti; Maalesef, örneğin, large_primorial + 3gereken asal sayıları belirlemeye çalışırken logaritmalarla çalışmak, kayan nokta problemleri etrafında çalışmak için yollar bulmam gerektiği anlamına geliyordu.

Ve bu, bu Liam'ın cevabının limanı .

Çevrimiçi deneyin!

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)]))

açıklama

Liam'ın cevabını anlatmaya çalışırken, şahsen verilen açıklamanın bir kısmının kafa karıştırıcı bir şekilde ifade edildiğini gördüm, bu yüzden bu onun algoritmasını açıklamaya çalışmam.

İlk olarak, kalıntılarını almak nve m.

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

Bu, girdi dizgilerinin tüm rakamlarını çevirmeyi ve bunları her bir asal sayımızı moduloya çevirmeyi içerir, örneğin 28 için, [(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))

Daha sonra kullanarak artıkları toplar, çoğaltır veya çıkarır eval()

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

Daha sonra artıklarımızın boyutlarını, yani ihtiyaç duyduğumuz minimum astar sayısını elde ederiz.

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

Boyutları almak en zor ve kod yoğunluğu olan bölümdür. Boyutu belirlemek için partial_chinesebize sırayı veren işlevi kullanırız u_i. u_iBir dakika içinde daha fazlası .

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

Dizisi u_i, her bir kalıntı alınarak hesaplanmıştır r_i, bir miktar çıkartılarak u_j * primorial(j) for j in [1, i)daha sonra, ve dividingile primorial(i), bütün modülo primes[i]. Bu u_i = (r_i - SUM) / primorial(i),. Anıtlarımız ve bölünme işlevlerimiz hakkında birazdan daha fazla.

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)hesaplar primorial(j) mod primes[i]. Bölüm herhangi asal modulo pbiz olası emin olabilir gibi kolayca el çarpımsal terslerinden kontrol ederek uygulanır u_iedilir 0 <= u_i < pp aralarında asal olması sağlanır ve böylece çarpımsal ters garantilidir.

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

Bütün bunlar bittikten sonra ipimizi yazdırdık ve bittik.

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

Sıradaki ne

Bunu uygulamak eğlenceliydi. Logaritmaları başka bir şekilde bir şekilde kullanıp kullanamayacağımı hala görmek istiyorum. Bu kodu veya APL veya Jelly gibi işlevsel bir golf dilinde olduğu gibi bir şey uygulamak istiyorum. Tüm golf önerileri ve düzeltmeleri kabul edilir!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.