Dans la foulée de nombreux défis, j'ai pensé que celui-ci pouvait être intéressant.
Dans ce défi, nous utiliserons le système de numération des résidus (RNS) pour effectuer l'addition, la soustraction et la multiplication sur de grands nombres entiers.
Qu'est-ce que le RNS
Le RNS est l'une des nombreuses façons que les gens ont développées pour identifier les nombres entiers. Dans ce système, les nombres sont représentés par une séquence de résidus (qui sont les résultats après une opération de module (c'est-à-dire le reste après la division entière)). Dans ce système, chaque entier a de nombreuses représentations. Pour garder les choses simples, nous allons limiter les choses afin que chaque entier soit représenté de manière unique. Je pense qu'il est plus facile de décrire ce qui se passe avec un exemple concret.
Examinons les trois premiers nombres premiers: 2, 3, 5. Dans le système RNS, nous pouvons utiliser ces trois nombres pour représenter de manière unique tout nombre inférieur à 2 * 3 * 5 = 30 en utilisant des résidus. Prenez 21:
21 est inférieur à 30, nous pouvons donc le représenter en utilisant les résultats après modding par 2, 3 et 5. (c'est-à-dire le reste après la division de l'entier par 2, 3 et 5)
Nous identifierions 21 avec la séquence d'entiers suivante:
21 ~ {21 mod 2, 21 mod 3, 21 mod 5} = {1, 0, 1}
Et donc dans notre système RNS, au lieu de "21", nous utiliserions {1,0,1}.
En général, étant donné un entier n , nous représentons n comme { n mod 2, ..., n mod p_k } où p_k est le plus petit premier tel que n est inférieur au produit de tous les nombres premiers inférieurs ou égaux à p_k .
Un autre exemple, disons que nous avons 3412. Nous devons utiliser 2,3,5,7,11,13 ici parce 2*3*5*7*11*13=30030
que, 2*3*5*7*11=2310
ce qui est trop petit.
3412 ~ {3412 mod 2, 3412 mod 3, 3412, mod 5, ..., 3412 mod 13} = {0, 1, 2, 3, 2, 6}
Vous remarquez qu'en utilisant ce système, nous pouvons représenter de très grands nombres relativement facilement. En utilisant {1, 2, 3, 4, 5, 6, 7, 8, ...} résidus, nous pouvons représenter des nombres jusqu'à {2, 6, 30, 210, 2310, 30030, 510510, 9699690 ...} respectivement. ( Voici la série )
Notre mission
Nous utiliserons ces résidus pour effectuer +, - et * sur de grands nombres. Je décrirai ces processus ci-dessous. Pour l'instant, voici les spécifications d'entrée et de sortie.
Contribution
Vous recevrez deux nombres (potentiellement très grands) via un argument stdin ou fonction. Ils seront donnés sous forme de chaînes de base 10 chiffres.
Afin de décrire le problème plus en détail, nous appelons la première entrée n
et la seconde m
. Supposons que n> m> = 0 .
Vous recevrez également +
ou -
ou *
pour indiquer l'opération à effectuer.
Sortie
Soit x un entier. Nous utiliserons [ x ] pour faire référence à la représentation RNS décrite ci-dessus de x .
Vous devez sortir [n] <operator> [m] = [result]
Comment effectuer les opérations dans RNS
Ces opérations sont relativement simples. Étant donné deux nombres en notation RNS, pour les ajouter, les soustraire ou les multiplier, effectuez simplement les opérations données par composant, puis prenez le module.
c'est à dire
{1, 2, 3} + {1, 1, 4} = {(1 + 1) mod 2, (2 + 1) mod 3, (3 + 4) mod 5} = {0, 0, 2}
Notez que si le nombre de résidus utilisé pour représenter deux nombres différents n'est pas le même, lors de l'exécution des opérations, vous devrez étendre le nombre "plus court" afin qu'il ait le même nombre de résidus. Cela suit le même processus. Voir les cas de test pour un exemple.
Il en va de même si le résultat nécessite plus de résidus que l'une ou l'autre entrée. Ensuite, les deux entrées doivent être "étendues".
Détails importants
Nous allons traiter ici de grands nombres, mais pas arbitrairement grands. Nous serons responsables des nombres jusqu'au produit des 100 premiers nombres premiers (voir ci-dessous). À cette fin, vous recevez gratuitement les 100 premiers nombres premiers (sans coût en octets) . Vous pouvez les coller dans un tableau appelé
p
ou quelque chose d'idiomatique dans votre langue, puis soustraire le nombre d'octets utilisés pour lancer ce tableau de votre total final. Cela signifie bien sûr qu'ils peuvent être codés en dur ou que vous pouvez utiliser un intégré pour les générer.Si pour une raison quelconque, c'est la représentation entière par défaut utilisée dans votre langue. C'est bon.
Vous ne pouvez pas utiliser de type entier de précision arbitraire, sauf s'il s'agit de la langue par défaut de votre langue. S'il s'agit de la valeur par défaut, vous ne pouvez pas l'utiliser pour stocker des entiers qui ne tiennent généralement pas en 64 bits.
Pour être clair, chaque entier sera toujours représenté avec le moins de résidus possible. Cela vaut pour l'entrée et la sortie.
Je pense que les autres spécifications devraient empêcher cela, mais pour être redondantes: vous ne pouvez pas effectuer l'opération donnée sur les entrées, puis tout changer en RNS puis en sortie. Vous devez remplacer les entrées par RNS, puis effectuer les opérations pour produire la sortie.
Cas de test
Contribution:
n = 10
m = 4
+
Sortie:
{ 0, 1, 0 } + { 0, 1 } = { 0, 2, 4 }
Explication:
Tout d'abord, changez chaque nombre en sa représentation RNS comme décrit ci-dessus:
10 ~ {0,1,0}
et 4 ~ {0,1}
. Notez que lorsque nous voulons effectuer une addition par composant, cela 10
a plus de composants que 4
. Par conséquent, nous devons "étendre" le nombre le plus court. Nous allons donc écrire brièvement 4 ~ {0,1} --> {0,1, 4 mod 5} = {0,1,4}
. Maintenant, nous procédons à l'addition, puis prenons le module.
- Contribution
n=28
m=18
+
Sortie:
[ 0, 1, 3 ] + [0, 0, 3 ] = [ 0, 1, 1, 4 ]
- Entrée (moi écrasant mon visage sur le clavier)
n=1231725471982371298419823012819231982571923
m=1288488183
*
Sortie (divisée sur des lignes distinctes pour plus de lisibilité):
[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
nécessite 28 nombres premiers. m
nécessite 10. n*m
nécessite 33.
- Contribution
n=8709668761379269784034173446876636639594408083936553641753483991897255703964943107588335040121154680170867105541177741204814011615930342030904704147856733048115934632145172739949220591246493529224396454328521288726490
m=1699412683745170450115957274739962577420086093042490863793456500767137147999161679589295549397604032154933975242548831536518655879433595016
-
Sortie:
[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
utilise 100 nombres premiers. m
utilise 70 nombres premiers. n-m
utilise 99 nombres premiers.
J'ai vérifié ceux-ci en utilisant l' ChineseRem
implémentation intégrée du théorème du reste chinois sur GAP (qui prend essentiellement des nombres RNS et les change en base 10 entiers). Je pense qu'ils ont raison. Si quelque chose semble louche, faites-le moi savoir.
Pour ceux qui s'en soucient, le produit des 100 premiers nombres premiers est:
471193079990618495316248783476026042202057477340967552018863483961641533584503
422120528925670554468197243910409777715799180438028421831503871944494399049257
9030720635990538452312528339864352999310398481791730017201031090
Ce nombre est 1 plus grand que le nombre maximal que nous pouvons représenter en utilisant le système donné (et 100 limitation principale).
(a,b,o)=>a.map((v,i)=>eval(v+o+b[i]))
en ES6 par exemple. Je pense que la partie la plus difficile est probablement de trouver le nombre de nombres premiers nécessaires pour représenter le résultat sans utiliser d'arithmétique de précision arbitraire, bien que la conversion ultérieure en RNS ne soit pas exactement triviale.
1234,1234,+
)?