Étant donné un entier n
, calculez un ensemble d' n
entiers uniques aléatoires dans la plage 1..n^2
(incluse) de telle sorte que la somme de l'ensemble soit égale àn^2
Aléatoire, dans ce cas, signifie uniformément aléatoire entre des sorties valides. Chaque sortie valide pour une donnée n
doit avoir une chance uniforme d'être générée.
Par exemple, n=3
devrait avoir une chance de chacun de 1/3 sortie 6, 1, 2
, 3, 5, 1
ou 4, 3, 2
. Comme il s'agit d'un ensemble, l'ordre n'est pas pertinent, 4, 3, 2
est identique à3, 2, 4
Notation
Le gagnant est le programme qui peut calculer le plus élevé n
en moins de 60 secondes.
Remarque: Pour éviter un éventuel codage partiel, toutes les entrées doivent être inférieures à 4000 octets
Essai
Tout le code sera exécuté sur ma machine Windows 10 locale (Razer Blade 15, 16 Go de RAM, Intel i7-8750H 6 cœurs, 4,1 GHz, GTX 1060 au cas où vous voudriez abuser du GPU), veuillez donc fournir des instructions détaillées pour exécuter votre code sur ma machine.
Sur demande, les entrées peuvent être exécutées soit via Debian sur WSL, soit sur une machine virtuelle Xubuntu (les deux sur la même machine que ci-dessus)
Les soumissions seront exécutées 50 fois de suite, le score final sera une moyenne des 50 résultats.
la source
Réponses:
Rouille , n ≈ 1400
Comment courir
Construisez avec
cargo build --release
et exécutez avectarget/release/arbitrary-randomness n
.Ce programme s'exécute le plus rapidement avec beaucoup de mémoire (tant qu'il ne change pas, bien sûr). Vous pouvez ajuster son utilisation de la mémoire en modifiant la
MAX_BYTES
constante, actuellement fixée à 8 Gio.Comment ça fonctionne
L'ensemble est construit par une séquence de décisions binaires (chaque nombre est à l'intérieur ou à l'extérieur de l'ensemble), dont chacune des probabilités est calculée de manière combinatoire en comptant le nombre d'ensembles possibles constructibles après chaque choix en utilisant la programmation dynamique.
L'utilisation de la mémoire pour les grands n est réduite en utilisant une version de cette stratégie de partitionnement binomial .
Cargo.toml
src/main.rs
Essayez-le en ligne!
(Remarque: la version TIO a quelques modifications. Premièrement, la limite de mémoire est réduite à 1 Gio. Deuxièmement, puisque TIO ne vous permet pas d'écrire un
Cargo.toml
et de dépendre de caisses externes commerand
, j'ai plutôt tirédrand48
de la bibliothèque C en utilisant le FFI. Je n'ai pas pris la peine de le semer, donc la version TIO produira le même résultat à chaque exécution. N'utilisez pas la version TIO pour le benchmarking officiel.)la source
ln_add_exp
en vérifiant si la différence absolue est supérieure à environ 15, ce qui peut être plus rapide s'il y a beaucoup d'ajouts.ln_add_exp
appels impliquent des entrées comparables.Java 7+, n = 50 dans ~ 30 sec sur TIO
Version non golfée de ma réponse pour la version code-golf de ce défi pour l'instant, avec un seul changement mineur:
java.util.Random#nextInt(limit)
est utilisé à la place d'(int)(Math.random()*limit)
un entier dans la plage[0, n)
, car il est environ deux fois plus rapide .Essayez-le en ligne.
Explication:
Approche utilisée:
Le code est divisé en deux parties:
n
nombres entiers aléatoires qui totalisentn squared
.L'étape 1 se fait avec les sous-étapes suivantes:
1) Générez un tableau de
n-1
nombres entiers aléatoires dans la plage[0, n squared)
. Et ajoutez0
etn squared
à cette liste. Cela se fait dans lesO(n+1)
performances.2) Ensuite, il triera le tableau avec le code intégré
java.util.Arrays.sort(int[])
, cela se fait enO(n*log(n))
termes de performances, comme indiqué dans les documents:3) Calculez la différence entre chaque paire. Cette liste résultante de différences contiendra des
n
nombres entiers qui se résument àn squared
. Cela se fait dans lesO(n)
performances.Voici un exemple:
Ces trois étapes ci-dessus sont donc plutôt bonnes pour les performances, contrairement à l'étape 2 et à la boucle autour de l'ensemble, qui est une force brute de base. L'étape 2 est divisée en ces sous-étapes:
1) La liste des différences est déjà enregistrée dans a
java.util.Set
. Il vérifiera si la taille de cet ensemble est égale àn
. Si c'est le cas, cela signifie que toutes les valeurs aléatoires que nous avons générées sont uniques.2) Et il vérifiera également qu'il ne contient pas
0
dans l'ensemble, car le défi demande des valeurs aléatoires dans la plage[1, X]
, oùX
estn squared
moins la somme de[1, ..., n-1]
, comme indiqué par @Skidsdev dans le commentaire ci-dessous.Si l'une des deux options ci-dessus (toutes les valeurs ne sont pas uniques ou un zéro est présent), elle générera un nouveau tableau et se remettra à zéro en revenant à l'étape 1. Cela continue jusqu'à ce que nous ayons un résultat. Pour cette raison, le temps peut varier considérablement. Je l'ai vu finir en 3 secondes une fois sur TIO pour
n=50
, mais aussi en 55 secondes une fois pourn=50
.Preuve d'uniformité:
Je ne sais pas vraiment comment prouver cela pour être complètement honnête. Le
java.util.Random#nextInt
est uniforme à coup sûr, comme décrit dans la documentation:Les différences entre ces valeurs aléatoires (triées) elles-mêmes ne sont bien sûr pas uniformes, mais les ensembles dans leur ensemble sont uniformes. Encore une fois, je ne sais pas comment le prouver mathématiquement, mais voici un script qui mettra les
10,000
ensembles générés (pourn=10
) dans une carte avec un compteur , où la plupart des ensembles sont uniques; certains ont répété deux fois; et l'occurrence répétée maximale se situe généralement dans la plage[4,8]
.Instructions d'installation:
Étant donné que Java est un langage assez bien connu avec de nombreuses informations disponibles sur la façon de créer et d'exécuter du code Java, je serai bref.
Tous les outils utilisés dans mon code sont disponibles en Java 7 (peut-être même déjà en Java 5 ou 6, mais utilisons 7 au cas où). Je suis à peu près sûr que Java 7 est déjà archivé, donc je suggère de télécharger Java 8 pour exécuter mon code.
Réflexions sur les améliorations:
Je voudrais trouver une amélioration pour la vérification des zéros et vérifier que toutes les valeurs sont uniques. Je pourrais vérifier
0
avant, en m'assurant que la valeur aléatoire que nous ajoutons au tableau n'est pas déjà dedans, mais cela signifierait quelques choses: le tableau devrait être unArrayList
afin que nous puissions utiliser la méthode intégrée.contains
; une boucle while devrait être ajoutée jusqu'à ce que nous trouvions une valeur aléatoire qui ne figure pas encore dans la liste. Étant donné que la vérification de zéro est désormais effectuée avec.contains(0)
sur l'ensemble (qui n'est vérifié qu'une seule fois), il est probablement préférable que les performances le vérifient à ce stade, par rapport à l'ajout de la boucle avec.contains
sur la liste, qui sera vérifiée au moins unen
fois , mais probablement plus.En ce qui concerne la vérification de l'unicité, nous n'avons que notre
n
quantité d'entiers aléatoires qui résumentn squared
après l'étape 1 du programme, donc seulement alors nous pouvons vérifier si tous sont uniques ou non. Il pourrait être possible de conserver une liste triable au lieu d'un tableau et de vérifier les différences entre les deux, mais je doute sérieusement que cela améliorera les performances que de simplement les mettre dans unSet
et vérifier si la taille de cet ensemble estn
une fois.la source
n^2 - sum(1..n-1)
par exemple pourn=5
le plus grand nombre valide est5^2 - sum(1, 2, 3, 4) == 25 - 10 == 15
n
, n'est-ce pas? Dans ce cas, vous pouvez ajouter0
à l'ensemble, puis vérifier que la taille est (maintenant) supérieure àn
. Cela ne peut se produire que si les différences sont toutes non nulles et distinctes.HashSet.contains
est dans la plupart des cas procheO(1)
, et dans le pire des cas, c'estO(n)
en Java 7 etO(log n)
en Java 8+ (il a été amélioré après avoir remplacé le chaînage par la détection de collision). Si je suis autorisé à renvoyer l'ensemble avec l'ajout0
pour la vérification, alors c'est en effet légèrement meilleur pour les performances, mais si je dois appelerset.remove(0);
à l'intérieur du if, je suis presque sûr que les performances sont quelque peu les mêmes.Mathematica n = 11
la source