Zezombye wrote:Le 17 septembre, je découvre la surprise dont parlaient les admins : le concours de rentrée.
J'ouvre le g1m, puis je me mets à décortiquer l'algorithme pour savoir comment est calculé le score.
On se rend compte rapidement que le calcul du score se fait dans cette boucle :
Show/Hide spoilerAfficher/Masquer le spoiler
- Code: Select all
While K!=48 And K!=47 And K!=44
If Abs Frac List 8[P] :Then
Int List 8[P]->List 8[P]
7->K
Else
GetKey->K
IfEnd
List 8[P]->Z
If K=28 And ImP Z<Ymax-T Or K=27 And ReP Z<Xmax Or K=38 And ReP Z>Xmin Or K=37 And ImP Z>Ymin Or K=7 :Then
Z+(K=27)-(K=38)+i((K=28)-(K=37))->List 8[P]
For 1->I To P-1+(K!=7)(M-P+1)
If I!=P :Then
S+Mat G[I,P](1/(1+Abs ((Mat G[I,P]>0 And I>1)F-Abs (List 8[I]-List 8[P])))-(K!=7)/(1+Abs ((Mat G[I,P]>0 And I>1)F-Abs (List 8[I]-Z))))->S
IfEnd
Next
IfEnd
PlotOff ReP Z,ImP Z
If K=78 Or K=77 Or K=7 :Then
If K=77 :Then
M-MOD(M-P+1,M-1)->P
Else
2+MOD(P-1,M-1)->P
IfEnd
PlotOff ReP List 8[P],ImP List 8[P]
PlotOn ReP Z,ImP Z
IfEnd
PlotChg ReP List 8[P],ImP List 8[P]
Text 1,1,S
WhileEnd
Plusieurs variables sont importantes ici :
- K pour Key, avec K=7 lors de l'initialisation
- S pour Score
- I pour Itérateur
- P pour Etoile (2 à 7, l'étoile 1 étant le centre de l'écran)
- F pour Distance (20, ne change pas).
On remarque que le code exécuté pour le recalcul du score est inutile ici, car on veut juste comprendre l'algo. Ainsi, on assume que K = 7 est toujours vrai.
En étudiant un peu plus l'algorithme, on remarque que la boucle While fonctionne ici comme un For qui itère sur P de 2 à 7. En pseudo-langage, ça donne :
Show/Hide spoilerAfficher/Masquer le spoiler
- Code: Select all
for (int P = 2; P <= 7; P++) {
for (int I = 1; I <= P-1; I++) {
1
S+Mat G[I,P]*(--------------------------------------------------
1+|20(Mat G[I,P]>0 And I>1)-|List 8[I]-List 8[P]||
K != 7
- -------------------------------------------) -> S
1+|20*(Mat G[I,P]>0 And I>1)-|List 8[I]-Z||
}
}
Mais comme K = 7, alors K != 7 est faux, donc on peut directement enlever cette portion du code :
Show/Hide spoilerAfficher/Masquer le spoiler
- Code: Select all
for (int P = 2; P <= 7; P++) {
for (int I = 1; I <= P-1; I++) {
1
S+Mat G[I,P]*(--------------------------------------------------)->S
1+|20(Mat G[I,P]>0 And I>1)-|List 8[I]-List 8[P]||
}
}
Tout de suite, c'est plus simple.
La matrice G vaut :
Show/Hide spoilerAfficher/Masquer le spoiler
- Code: Select all
1 2 3 4 5 6 7
1 0 485402 366483 895650 398681 246960 1062990
2 0 2603497 -18533 -3484358 386459 -768468
3 0 -646585 -3156512 1487979 -2522960
4 0 2602703 632508 -2423677
5 0 -1746276 1331355
6 0 -2905103
7 0
À noter que G[X,Y] = G[Y,X] (avec l'instruction Mat G+Trn Mat G->Mat G).
Pour avoir le meilleur score, il faut donc influer sur le dénominateur de la fraction :
1+|20(Mat G[I,P]>0 And I>1)-|List 8[ I ]-List 8[P]||
.
Ici,
|List 8[ I ]-List 8[P]|
est la distance entre I et P. Deux cas sont possibles :
- Si Mat G[I,P] > 0 : dans ce cas il faut que le dénominateur soit le plus proche de 1. En étudiant bien le dénominateur, il faut que
|20(Mat G[I,P]>0 And I>1)-|List 8[ I ]-List 8[P]|| = 0
, ce qui signifie que la distance entre I et P doit être la plus proche de 20.
- Si Mat G[I,P] <= 0 ou que I = 1 (on calcule le score par rapport au centre), alors plus I sera proche de P, plus la fraction sera proche de 1. Cela veut dire que si Mat G[I,P] <= 0 alors il faut que I soit le plus éloigné possible de P, et si I = 1 alors il faut que I soit aux mêmes coordonnées que P.
Toutefois, mon cerveau a décidé pour une quelconque raison de lire la condition
(Mat G[I,P]>0 And I>1)
en tant que
(Mat G[I,P]=0 And I>1)
, ce qui change tout. Cela veut dire que si Mat G[I,P] < 0, alors I doit être à une distance éloignée de 20 de P, donc I peut être aux mêmes coordonnées que P avec une perte de score minime ! (ce qui n'est pas le cas, mais c'est ce que je croyais au début).
J'ai donc tracé le graphe des liaisons entre les étoiles :
Puis, je me suis rendu compte qu'il n'y avait qu'une seule configuration possible pour qu'il n'y ait que des traits verts reliant les étoiles :
La seule modification ici était d'influer sur l'angle du triangle 6-2-3 (par rapport à l'horizontale), ce qui avait un impact car G[2,4] = -18533 alors que G[3,4] = -646585. Après un bruteforce, je trouve mon score de 9 843 347,30939981. Bizarrement, mon algorithme trouve un score de 9.6 millions (mais qui me donne 9.8M lorsque je transpose la liste sur la calculatrice), mais j'ai attribué ça à un changement de moteur de calcul.
Convaincu que le seul moyen de battre mon score n'était que de quelques millièmes en changeant un peu l'angle du triangle 6-2-3, je cherche sur TI et HP, mais n'arrive qu'à faire 46M et 123M, loin des premiers.
---
Un mois plus tard, le 28 octobre, Nemhardy me donne un coup de pied au cul en sortant un score de 9 966 747. Cela implique une toute nouvelle configuration, ce que je trouve bizarre : il n'y a pas de moyen évident d'arranger les étoiles autre que ma configuration.
En cherchant un peu, je trouve qu'en rompant la liaison 4-6 et en plaçant le 4 sur le 6, cela pourrait faire augmenter mon score :
- Rompre la liaison 4-6 me fait perdre 632k
- Placer le 4 sur le centre (avec le 7) me fait gagner 895k
- La pénalité de 4 et 7 est divisée par 21, ça me fait perdre 2423/21 ~= 120k.
Tout cela s'additionne pour me donner une amélioration d'environ 120k, ce qui correspond à peu près au delta de 123k entre mon score et celui de Nemhardy.
Je teste, et je trouve un score de... 7 millions ?! Bizarre. Je refais mes calculs : seuls les 3 paramètres cités varient. Je retélécharge Galactik au cas où j'aurais modifié le calcul du score dans un de mes tests : même chose. Je teste sur Graph 90+E (en devant en plus démarrer ma VM, car j'avais épuisé la période d'essai de l'émulateur) : même chose.
Aurais-je fait une erreur dans le recopiage de l'algorithme ? Je regarde, et je ne vois pas. Je remarque que le score de 7 millions était comme si la pénalité de la liaison 7-4 était appliquée sans être divisée par 20... il doit y avoir une erreur, car G[7,4] != 0 et I > 1 donc la condition devrait être vraie.
Je teste : P = 4, I = 7, Mat G[I,P] = -2M, alors pourquoi (Mat G[I,P] = 0 And I>1) retourne 0...
...
...Ah, c'est Mat G[I,P] > 0.
(oui, il m'a fallu jusqu'au dernier moment pour que mon cerveau corrige l'erreur)
Maintenant que je connais le vrai fonctionnement de l'algorithme, une nouvelle configuration semble logique : en effet, cela veut dire que pour des étoiles à 20 de distance, les liaisons négatives sont divisées par 21.
La liaison 4-2 n'imposant qu'une pénalité de -18k, je peux les superposer, ce qui me donne une amélioration de -1063k + 896k + 485k = 318k, et la configuration suivante :
L'amélioration n'est que de 147k en raison du rapprochement des liaisons 5-3, 3-4 et 5-6, qui font sentir leurs millions de pénalités.
Un autre bruteforce pour trouver l'angle du triangle 2-3-6 (et cette fois mon algo en java trouve le même score qu'affiché sur la calculatrice), et je trouve un score de 9 991 310, qui est d'ailleurs toujours premier de la catégorie casio.
---
Avec ces connaissances en plus, j'ai re-regardé mes configurations TI et HP, que je triturais pendant un mois (mais avec un algo faux). Je me suis aidé des images des matrices :
Sur HP, je n'ai pas réussi à bien améliorer mon score : 125M contre 123M... et de toute façon la catégorie était saturée, avec 3 participants différents étant tombés sur ce qui est visiblement le score maximal.
Sur TI, après un peu d'expérimentation, je suis tombé sur cette configuration :
Ce qui me donnait 48M... pas assez, mais suffisant pour être 2ème.
Pour modifier la configuration, il faut noter que le polygone 2-11-6-10-7 est inaltérable (car composé uniquement de triangles verts) ; impossible de déplacer n'importe laquelle de ces 5 étoiles sans baisser mon score.
J'ai essayé de relier la liaison 8-5, mais c'est impossible, car il fallait alors superposer des étoiles avec une liaison négative, ou casser des liaisons positives, ce qui baissait mon score (la liaison 8-5 ne m'apportant que 1.22M).
Mais, en mettant le 7 sur le 3, il est possible de changer la structure tout en gardant l'intégralité des liaisons vertes :
On voit que, tout comme casio, on peut influer sur l'angle de l'étoile 8 par rapport à l'étoile 10. Un petit bruteforce plus tard, j'ai un score de 49 942 613, ce qui me classe premier. Mais le participant n°23 avait soumis un score de 49 946k avant de se rabattre vers HP - il y avait donc une amélioration à faire.
Y a-t-il quelque chose qui pourrait bouger dans notre configuration ? On élimine tous les triangles, il reste donc le losange 9-5-7-4 dont on peut modifier la distance 5-4 afin d'améliorer le score.
Un peu de trigonométrie pour déterminer les coordonnées de 9 par rapport à l'angle de 4 par rapport à 7, et avec un bruteforce, je trouve 49 946k.
Mais étant donné qu'il y a 2 angles à modifier : l'angle de 4 par rapport à 7, et l'angle de 8 par rapport à 10, il faut bruteforcer les 2 afin de trouver la meilleure configuration.
Un premier bruteforce et je trouve 49 946 730.080507. Un bon début, mais il me reste 133 millionièmes.
En raffinant, je me rapproche : 509, 543, puis un score de 639, que je soumets.
45 mn plus tard, je trouve un score de... 641 ! J'obtiens alors la première place au classement TI, et y reste.
Durant les prochains jours, je tente de trouver un 642, sans succès. Mon bruteforce atteint les limites, et la différence de moteur de calcul se fait sentir : des scores supposés être supérieurs à mon 641 se traduisent par un 640, 639, ou pire, .073543 (7 millièmes de moins).
J'essaie donc le BigDecimal pour garder un moteur de calcul décimal, mais... c'est lent. 1 heure pour 10^6 combinaisons, alors que je peux faire 10^9 combinaisons (voire plus, je ne me souviens plus) en flottant. Et bruteforcer sur la TI-83, n'en parlons pas.
L'interpréteur Lua de la NSpire semble une bonne option... sauf qu'il calcule en flottant lui aussi, et non pas en décimal. Il fait donc les mêmes erreurs que mon algorithme Java.
Je décide d'en rester là, en me disant que, si quelqu'un trouve un 642, je reviendrai sur Casio... mais visiblement mon 641 était le maximum possible.
Voilà, et encore merci à Nemh qui m'a permis de me rendre compte du vrai algo - sinon je m'en serais rendu compte 4 jours plus tard, et ces 4 jours auraient pu être fatals x)