Le nombre de diviseurs de 120 est 16. En fait, 120 est le plus petit nombre avec 16 diviseurs.
Trouvez le plus petit nombre avec 2 ** 500500 diviseurs. Donnez votre réponse modulo 500500507.
C'est assez simple de compter les diviseurs de n, par exemple. en Python len([i for i in range(1,n+1) if n % i == 0])
. C'est O (n).
J'ai essayé la recherche par force brute et j'ai trouvé que le plus petit nombre avec 32 diviseurs est 840, mais c'est beaucoup trop lent pour le problème ci-dessus. De l'inégalité count_divisors(n) <= n
, le nombre va être énorme.
Qu'est-ce que tu penses? Des idées? Comment calculer le plus petit nombre avec un certain nombre de diviseurs?
Edit: Je ne pense pas que ce soit un doublon. Cette question est plus spécifique, elle concerne une classe particulière de beaucoup plus grands nombres. La autre question demande en général. Ses réponses ne sont pas applicables à ce problème, c'est une ampleur différente.
Vous devriez utiliser la formule pour le nombre de diviseurs de nombre entier n
:
d(n) = (a1+1) (a2+1) ... (ak+1)
où
n = p1une1 * p2une2 * p3une3 * ... * pkunek
est une représentation unique de chaque entier grâce aux puissances de ses principaux diviseurs. Ceci est une formule bien connue, mais si on se demande comment l'obtenir, notez que d
divise n
si et seulement si d
est de la forme p1x1 * p2x2 * p3x3 * ... * pkxk, où chacun de xje est compris entre 0 et aje, donc il y aje + 1 possibilités pour choisir chacun des xje. Maintenant, appliquez simplement la règle de produit et vous obtenez la formule souhaitée.
Pour d(n)
fixe (comme dans votre cas), la valeur minimale de n
est évidemment obtenue en sélectionnant avec soin les puissances des nombres premiers existants ou en ajoutant de nouveaux nombres premiers. Travaillons à travers cet exemple simple, 16:
d (x) = (a1+1) (a2+1) ... (ak+1) = 16 = 24.
Cela signifie que vous avez au plus 4 nombres premiers différents, donc:
x = 2une1 * 3une2 * 5une3 * 7une4
où unje > = 0. La question est maintenant - pour obtenir la valeur minimale de x
, vaut-il mieux augmenter les puissances de 2 (c.-à-d., Incrémenter a1), ou d’utiliser 7 (c’est-à-dire prendre4= 1 au lieu d'un4= 0)? Eh bien, c'est simple à vérifier, 2 * 3 * 5 * 7> 23 * 3 * 5 = 120, c'est pourquoi le 120 est la réponse dans ce cas.
Comment généraliser cette approche? Vous devez créer min-heap où vous placerez les puissances des nombres premiers, en veillant à ce que le nombre de diviseurs atteigne la valeur spécifiée. Dans le cas de 16, ce min-tas contiendrait les nombres 2, 3, 5, 7, 22, 32, 24 etc. Pourquoi? Parce que 16 = 24, donc chacun de (unje+1) doit diviser 16, c'est-à-dire qu'il doit avoir une puissance de 2. Chaque fois que vous ajoutez un nouveau pouvoir, le côté gauche (c'est-à-dire la variable d(x)
) doit être augmenté de 2, trouver le plus petit nombre avec 2500500 diviseurs. Le segment de mémoire est initialisé avec les premiers nombres premiers k
(dans l'instruction de problème, k = 500500
), et à chaque étape, lorsque vous ouvrez px du tas, p2x est retourné et le résultat est multiplié par px. Solution pas à pas pour d(x)
= 16 = 24:
Step Heap d(x) x
==========================
0 2,3,5,7 1 1
1 3,4,5,7 2 2
2 4,5,7,9 4 6
3 5,7,9,16 8 24
4 7,9,16,25 16 120
HTH.
Voici le résumé de mon Javascript - où factorCount
représente le nombre de diviseurs:
factorCount
nombre de diviseurs Voici une description détaillée du code de mes fonctions JavaScript:
var primeFactors = findPrimeFactors(factorCount);
var primeFactorCombinations = removeDuplicateArrays(generateCombinations(primeFactors, 1));
var combinedFactorCandidates = generateCombinedFactorCombinations(primeFactors, primeFactorCombinations);
var smallestNumberWithFactorCount = determineMinimumCobination(combinedFactorCandidates);
Et voici le sha-bang complet:
function smallestNumberByFactorCount(factorCount) {
function isPrime(primeCandidate) {
var p = 2;
var top = Math.floor(Math.sqrt(primeCandidate));
while(p<=top){
if(primeCandidate%p === 0){ return false; }
p++;
}
return true;
}
function findPrimeAfter(currentPrime) {
var nextPrimeCandidate = currentPrime + 1
while(true) {
if(isPrime(nextPrimeCandidate)){
return nextPrimeCandidate;
} else {
nextPrimeCandidate++;
}
}
}
function findPrimeFactors(factorParent) {
var primeFactors = [];
var primeFactorCandidate = 2;
while(factorParent !== 1){
while(factorParent % primeFactorCandidate === 0 && factorParent !== 1 ){
primeFactors.Push(primeFactorCandidate);
factorParent /= primeFactorCandidate;
}
primeFactorCandidate = findPrimeAfter(primeFactorCandidate);
}
return primeFactors;
}
function sortArrayByValue(a,b){
return a-b;
}
function clone3DArray(arrayOfArrays) {
var cloneArray = arrayOfArrays.map(function(arr) {
return arr.slice();
});
return cloneArray;
}
function doesArrayOfArraysContainArray(arrayOfArrays, array){
var aOA = clone3DArray(arrayOfArrays);
var a = array.slice(0);
for(let i=0; i<aOA.length; i++){
if(aOA[i].sort().join(',') === a.sort().join(',')){
return true;
}
}
return false;
}
function removeDuplicateArrays(combinations) {
var uniqueCombinations = []
for(let c=0; c<combinations.length; c++){
if(!doesArrayOfArraysContainArray(uniqueCombinations, combinations[c])){
uniqueCombinations[uniqueCombinations.length] = combinations[c];
}
}
return uniqueCombinations;
}
function generateCombinations(parentArray, minComboLength) {
var generate = function(n, src, got, combinations) {
if(n === 0){
if(got.length > 0){
combinations[combinations.length] = got;
}
return;
}
for (let j=0; j<src.length; j++){
generate(n - 1, src.slice(j + 1), got.concat([src[j]]), combinations);
}
return;
}
var combinations = [];
for(let i=minComboLength; i<parentArray.length; i++){
generate(i, parentArray, [], combinations);
}
combinations.Push(parentArray);
return combinations;
}
function generateCombinedFactorCombinations(primeFactors, primeFactorCombinations) {
var candidates = [];
for(let p=0; p<primeFactorCombinations.length; p++){
var product = 1;
var primeFactorsCopy = primeFactors.slice(0);
for(let q=0; q<primeFactorCombinations[p].length; q++){
product *= primeFactorCombinations[p][q];
primeFactorsCopy.splice(primeFactorsCopy.indexOf(primeFactorCombinations[p][q]), 1);
}
primeFactorsCopy.Push(product);
candidates[candidates.length] = primeFactorsCopy.sort(sortArrayByValue).reverse();
}
return candidates;
}
function determineMinimumCobination (candidates){
var minimumValue = Infinity;
var bestFactorCadidate = []
for(let y=0; y<candidates.length; y++){
var currentValue = 1;
var currentPrime = 2;
for(let z=0; z<combinedFactorCandidates[y].length; z++){
currentValue *= Math.pow(currentPrime,(combinedFactorCandidates[y][z])-1);
currentPrime = findPrimeAfter(currentPrime);
}
if(currentValue < minimumValue){
minimumValue = currentValue;
bestFactorCadidate = combinedFactorCandidates[y];
}
}
return minimumValue;
}
var primeFactors = findPrimeFactors(factorCount);
var primeFactorCombinations = removeDuplicateArrays(generateCombinations(primeFactors, 1));
var combinedFactorCandidates = generateCombinedFactorCombinations(primeFactors, primeFactorCombinations);
var smallestNumberWithFactorCount = determineMinimumCobination(combinedFactorCandidates);
return smallestNumberWithFactorCount;
}
Collez le bloc de code ci-dessus dans la console de votre navigateur et vous pourrez le tester vous-même:
> smallestNumberByFactorCount(3) --> 4
> smallestNumberByFactorCount(4) --> 6
> smallestNumberByFactorCount(5) --> 16
> smallestNumberByFactorCount(6) --> 12
> smallestNumberByFactorCount(16) --> 120
> smallestNumberByFactorCount(100) --> 45360
> smallestNumberByFactorCount(500) --> 62370000
> smallestNumberByFactorCount(5000) --> 4727833110000
> smallestNumberByFactorCount(100000000) --> 1.795646397225103e+40
Mon algorithme commence à chier lorsque l'entrée atteint environ 100 millions ... Pour le problème Euler 500 du projet Euler où l'entrée serait 2 ^ 500500 (un très grand nombre), vous auriez besoin d'une autre approche. Cependant, c'est une bonne approche générale qui vous mène assez loin. J'espère que ça aide.
S'il vous plaît laissez des commentaires avec des suggestions d'optimisation de l'efficacité. J'aimerais les entendre.
Ouf, je viens de le résoudre en Java. Mon "plus petit nombre avec 2 ^ n diviseurs" a fini par être représenté par une carte des nombres premiers et de leurs pouvoirs.
Ce casse-tête concerne autant l'optimisation que tout autre chose: faites travailler votre code, puis remettez-le à jour. Il vaut vraiment la peine de tester environ 2 ^ 30 diviseurs, car il est possible d'insérer toutes sortes de bogues de second ordre lors de l'optimisation. Réutilisez les résultats de calculs antérieurs, essayez de ne rien trier et arrêtez d'itérer dès que vous le pouvez (NavigableSet et LinkedHashMap étaient utiles). Je ne vais pas apprendre à ma grand-mère à tester efficacement la primalité.
J'ai longtemps utilisé Java, mais avec des nombres de cette taille, il est facile de passer par Long.MAX_VALUE au milieu d'un calcul, rappelez-vous: (A ^ 2 * B)% C = (A * ((A * B)% C ))% C.
J'espère que tout cela motive plutôt que de laisser tomber le jeu. Continue!
Pas une réponse complète à la place quelques astuces:
le diviseur entier max de n
est n/2
donc pas besoin de vérifier tous les diviseurs jusqu'à n
peut utiliser une décomposition principale
le diviseur principal maximal correspond à sqrt(n)
. Il n'est donc pas nécessaire de tester jusqu'à n
, mais plutôt jusqu'à sqrt(n)
ou jusqu'à un nombre égal à la moitié des n
bits
m=(2^(ceil(ceil(log2(n))/2)+1))-1
cela devrait accélérer un peu les choses, mais vous devez ajouter le calcul des diviseurs non principaux
cela ressemble à une sorte de série (décomposition principale)
divisors | prime_divisors | non_prime_divisors | LCM(all divisors)
1 | 1 | | 1
2 | 1,2 | | 2
3 | 1,2 | 4 | 4
4 | 1,2,3 | 6 | 6
5 | 1,2 | 4,8,16 | 16
6 | 1,2,3 | 4,6,12 | 12
7 | 1,2 | 4,8,16,32,64 | 64
8 | 1,2,3 | 4,6,8,12,24 | 24
...
16 | 1,2,3,5 |4,6,8,10,12,15,20,24,30,40,60,120 | 120
essayez donc de trouver l'équation qui génère cet ordre, puis calculez simplement l'itération n-th
dans l'arithmétique modulo (opération PI simple ... modmul
). Je peux voir que les éléments pairs et impairs ont une équation séparée ...
[edit1] décomposer jusqu'à 16 diviseurs
1: 1
2: 1, 2
3: 1, 2, 4
4: 1, 2, 3, 6
5: 1, 2, 4, 8, 16
6: 1, 2, 3, 4, 6, 12
7: 1, 2, 4, 8, 16, 32, 64
8: 1, 2, 3, 4, 6, 8, 12, 24
9: 1, 2, 3, 4, 6, 9, 12, 18, 36
10: 1, 2, 3, 4, 6, 8, 12, 16, 24, 48
11: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512,1024
12: 1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60
13: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512,1024,2048,4096
14: 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 192
15: 1, 2, 3, 4, 6, 8, 9, 12, 16, 18, 24, 36, 48, 72, 144
16: 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 24, 30, 40, 60, 120
Comme l'explique Miljen Mikic, la fonction de dénombrement des diviseurs est déterminée par la factorisation en facteurs premiers. Pour calculer n, commencez à 1 et utilisez un algorithme glouton pour doubler le nombre de diviseurs k fois en choisissant le facteur le moins cher à chaque étape. Les coûts initiaux sont les nombres premiers, remplacés par leur carré lorsque vous les utilisez. Après avoir précalculé les k premiers nombres premiers, vous pouvez le faire rapidement avec un min-tas. En python
import primesieve # pip install primesieve
import heapq
def solve(k, modulus=None):
"""Calculate the smallest number with 2**k divisors."""
n = 1
costs = primesieve.generate_n_primes(k) # more than necessary
for i in range(k):
cost = heapq.heappop(costs)
heapq.heappush(costs, cost**2)
n *= cost
if modulus:
n %= modulus
return n
assert solve(4) == 120
if __== "__main__":
print(solve(500500, 500500507))
Le diviseur le plus élevé que tout nombre a, autre que lui-même, est la moitié du nombre. Par exemple, 120 a un diviseur maximal de 60 autre que lui-même. Ainsi, vous pouvez facilement réduire la plage de (n + 1) à (n/2).
De plus, pour qu'un nombre ait m diviseurs, le nombre doit être au moins ((m-1) * 2) en suivant la logique ci-dessus (-1 car le m e nombre est lui-même). Par exemple, un nombre avec 4 diviseurs doit être au moins de 6. Ainsi, votre recherche pour n a une plage plus petite maintenant.
Ces deux réduiront un peu la durée d'exécution.