Disons que vous avez un dé à 20 faces. Vous commencez à lancer ce dé et vous devez le lancer quelques dizaines de fois avant de finalement lancer les 20 valeurs. Vous vous demandez combien de rouleaux ai-je besoin avant d'avoir 50% de chances de voir les 20 valeurs? Et combien de rouleaux de n
dé recto dois-je lancer avant de lancer tous les n
côtés?
Après quelques recherches, vous découvrez qu'il existe une formule pour calculer la probabilité de rouler toutes les n
valeurs après les r
rouleaux.
P(r, n) = n! * S(r, n) / n**r
où S(a, b)
dénote les nombres de Stirling du deuxième type , le nombre de façons de partitionner un ensemble de n objets (chaque rouleau) en k sous-ensembles non vides (chaque côté).
Vous trouverez également la séquence OEIS , que nous appellerons R(n)
, qui correspond à la plus petite r
où P(r, n)
est d'au moins 50%. Le défi consiste à calculer le n
terme de cette séquence le plus rapidement possible.
Le défi
- Étant donné un
n
, trouvez le plus petitr
oùP(r, n)
est supérieur ou égal à0.5
ou 50%. - Votre code devrait théoriquement gérer tout entier non négatif
n
en entrée, mais nous ne testerons votre code que dans la plage de1 <= n <= 1000000
. - Pour la notation, nous allons prendre le temps total nécessaire pour faire fonctionner
R(n)
sur les intrants1
par10000
. - Nous vérifierons si vos solutions sont correctes en exécutant notre version de
R(n)
sur votre sortie pour voir siP(your_output, n) >= 0.5
etP(your_output - 1, n) < 0.5
, c'est-à-dire que votre sortie est réellement la plus petiter
pour une donnéen
. - Vous pouvez utiliser n'importe quelle définition pour
S(a, b)
dans votre solution. Wikipedia a plusieurs définitions qui peuvent être utiles ici. - Vous pouvez utiliser des fonctions intégrées dans vos solutions, y compris celles qui calculent
S(a, b)
ou même celles qui calculentP(r, n)
directement. - Vous pouvez coder en dur jusqu'à 1000 valeurs
R(n)
et un million de nombres de Stirling, mais aucune de ces limites n'est stricte et peut être modifiée si vous pouvez faire un argument convaincant pour les augmenter ou les diminuer. - Vous n'avez pas besoin de vérifier tous les possibles
r
entren
et ceux quer
nous recherchons, mais vous devez trouver le plus petitr
et pas n'importer
oùP(r, n) >= 0.5
. - Votre programme doit utiliser un langage librement exécutable sur Windows 10.
Les spécifications de l'ordinateur qui testera vos solutions sont i7 4790k, 8 GB RAM
. Merci à @DJMcMayhem d' avoir fourni son ordinateur pour les tests. N'hésitez pas à ajouter votre propre timing non officiel pour référence, mais le timing officiel sera fourni plus tard une fois que DJ pourra le tester.
Cas de test
n R(n)
1 1
2 2
3 5
4 7
5 10
6 13
20 67 # our 20-sided die
52 225 # how many cards from a huge uniformly random pile until we get a full deck
100 497
366 2294 # number of people for to get 366 distinct birthdays
1000 7274
2000 15934
5000 44418
10000 95768
100000 1187943
1000000 14182022
Faites-moi savoir si vous avez des questions ou des suggestions. Bonne chance et bonne optimisation!
la source
Réponses:
Python + NumPy, 3,95 secondes
Essayez-le en ligne!
Comment ça fonctionne
Celui-ci utilise la série de formes fermées pour P ( r , n ) et sa dérivée par rapport à r , réarrangée pour la stabilité numérique et la vectorisation, pour effectuer une recherche de méthode de Newton pour r telle que P ( r , n ) = 0,5, arrondi r à un entier avant chaque étape, jusqu'à ce que l'étape déplace r de moins de 3/4. Avec une bonne estimation initiale, cela ne prend généralement qu'une ou deux itérations.
x i = log (1 - i / n ) = log (( n - i ) / n )
cx i = log ( n ! / (( n - i - 1)! ⋅ n i + 1 )
y i = cx i + cx n - i - 2 - cx n - 1 = log binom ( n , i + 1)
z i = (-1) i + 1 ⋅ binom ( n ,i + 1) ⋅ (( n - i - 1) / n ) r
1 + ∑ z i = n! ⋅ S ( r , n ) / n r = P ( r , n )
z i ⋅ x i + 1 = (-1) i + 1 ⋅ binom ( n , i + 1) ⋅ (( n - i - 1) / n ) r log (( n - i - 1) / n)
∑ z i ⋅ x i + 1 = d / d r P ( r , n )
la source
0.366512
c'étaitlog
quelque chose d'il y a longtemps. Va utiliser-log(log(2)
dans ma prochaine itération. Deuxièmement, l'idée d'utiliser la méthode de Newton est également très intelligente et je suis heureux de voir que cela fonctionne si bien. Troisièmement, je vais presque certainement volerexp(log(binom(n, i+1)) + r * log((n-i-1)/n))
: P Bravo pour une excellente réponse! : Dnumpy
importation enfrom numpy import *
et pour une raison quelconque, le temps est passé à 0 ... Essayez-le en ligne ?r
, car votre approximation initiale est déjà assez bonne. Au plaisir de vous revoir au PPCG, et désolé pour tout.