Bref, du fromage en morceau que je rĂąpe moi-mĂȘme... ou que je demande au fromager de rĂąper et de mettre dans une gamelle qui m'appartient. De l'achat en vrac quoi.
fromcollections.abcimportIterable,Iterator,SequencefromtypingimportSelfimportnumpyasnpimportnumpy.typingasnptimportaocclassCity:def__init__(self,array:Sequence[Sequence[int]]):self.matrix:npt.NDArray[np.ubyte]=np.array(array,dtype=np.ubyte)self.ly,self.lx=self.matrix.shape@classmethoddefimport_lines(cls,lines:Iterable[str])->Self:array=[[int(char)forcharinlineifchar!='\n']forlineinlines]returncls(array)defwalk(self,minturn,maxturn)->int:# We will be using a matrix with three coordinates:# * z indicates how many steps were made in which direction:# - z < 0: starting, no previous steps!# - 0 †z < maxturn: 1 to maxturn steps up â# - maxturn †z < 2*maxturn: 1 to maxturn steps down â# - 2*maxturn †z < 3*maxturn: 1 to maxturn steps left â# - 3*maxturn †z < 4*maxturn: 1 to maxturn steps right âvisits:npt.NDArray[np.int_]=np.full((4*maxturn,self.ly,self.lx),-1,dtype=np.int_)def_neighs(z:int,y:int,x:int)->Iterator[tuple[int,int,int]]:"""Yield all directly accessible neighbors of a point, including points outside the limits of the city."""ifz<0:# Special case for startyield(0*maxturn,y-1,x)yield(1*maxturn,y+1,x)yield(2*maxturn,y,x-1)yield(3*maxturn,y,x+1)returndiv,mod=divmod(z,maxturn)ifdiv==0:ifmod<maxturn-1:yield(z+1,y-1,x)ifmod+1>=minturn:yield(2*maxturn,y,x-1)yield(3*maxturn,y,x+1)returnifdiv==1:ifmod<maxturn-1:yield(z+1,y+1,x)ifmod+1>=minturn:yield(2*maxturn,y,x-1)yield(3*maxturn,y,x+1)returnifdiv==2:ifmod+1>=minturn:yield(0*maxturn,y-1,x)yield(1*maxturn,y+1,x)ifmod<maxturn-1:yield(z+1,y,x-1)returnifdiv==3:ifmod+1>=minturn:yield(0*maxturn,y-1,x)yield(1*maxturn,y+1,x)ifmod<maxturn-1:yield(z+1,y,x+1)returndefneighs(z:int,y:int,x:int)->Iterator[tuple[int,int,int]]:forz_,y_,x_in_neighs(z,y,x):if0<=x_<self.lxand0<=y_<self.ly:yieldz_,y_,x_currents:dict[tuple[int,int,int],int]={(-1,0,0):0}whilelen(currents)>0:nexts:dict[tuple[int,int,int],int]={}forcurrent_pos,current_totalincurrents.items():fornext_posinneighs(*current_pos):z,y,x=next_posloss=self.matrix[y,x]next_total=current_total+lossif(visits[next_pos]<0ornext_total<visits[next_pos]):visits[next_pos]=next_totalnexts[next_pos]=next_totalcurrents=nextsloss=-1fordivinrange(4):formodinrange(minturn-1,maxturn):z=div*maxturn+modpos=(z,self.ly-1,self.lx-1)ifloss<0or0<visits[pos]<loss:loss=visits[pos]returnlossdefpart1(lines:aoc.Data)->int:"""Solve puzzle part 1: determine the minimum total heat loss from start to end, using normal crucibles"""city=City.import_lines(lines)returncity.walk(1,3)defpart2(lines:aoc.Data)->int:"""Solve puzzle part 2: determine the minimum total heat loss from start to end, using ultra crucibles"""city=City.import_lines(lines)returncity.walk(4,10)
importdataclassesimportenumimportioimportrefromcollections.abcimportIterable,IteratorfromtypingimportClassVar,Selfimportnumpyasnpimportnumpy.typingasnptimportaocCoords=tuple[int,int]Vector=tuple[int,int]classDirection(enum.Enum):UP='U'DOWN='D'LEFT='L'RIGHT='R'@propertydefvector(self)->Vector:ifselfisself.UP:return(-1,0)ifselfisself.DOWN:return(1,0)ifselfisself.LEFT:return(0,-1)ifselfisself.RIGHT:return(0,1)assertFalse,"we covered all cases"deftranslate(self,position:Coords)->Coords:y,x=positiondy,dx=self.vectorreturny+dy,x+dx@dataclasses.dataclass(frozen=True)classInstruction:direction:Directionlength:intimport_pattern1:ClassVar[re.Pattern]=re.compile(r'^([UDLR]) (\d+) \(#[0-9A-Fa-f]{6}\)\n?$')import_pattern2:ClassVar[re.Pattern]=re.compile(r'^[UDLR] \d+ \(#([0-9A-Fa-f]{5})([0-9A-Fa-f])\)\n?$')@classmethoddefimport_line1(cls,line:str)->Self:if(m:=cls.import_pattern1.match(line))isnotNone:returncls(Direction(m[1]),int(m[2]))raiseValueError("invalid instruction line")@classmethoddefimport_line2(cls,line:str)->Self:if(m:=cls.import_pattern2.match(line))isnotNone:length=int(m[1],16)ifm[2]=='0':direction=Direction.RIGHTelifm[2]=='1':direction=Direction.DOWNelifm[2]=='2':direction=Direction.LEFTelifm[2]=='3':direction=Direction.UPelse:raiseValueError("invalid instruction line")returncls(direction,length)raiseValueError("invalid instruction line")defapply(self,position:Coords)->Coords:y,x=positiondy,dx=self.direction.vectorreturny+self.length*dy,x+self.length*dxclassTerrain1:def__init__(self,instructions:Iterable[Instruction])->None:position:Coords=(0,0)excavated:set[Coords]={(0,0)}forinstructionininstructions:for_inrange(instruction.length):position=instruction.direction.translate(position)excavated.add(position)ymin,ymax=0,0xmin,xmax=0,0for(y,x)inexcavated:ymin=min(ymin,y)ymax=max(ymax,y)xmin=min(xmin,x)xmax=max(xmax,x)# Time to write a terrain matrix# It needs to span the entire excavated area, plus a margin:# * vertically: from ymin - 1 to ymax + 1 included;# * horizontally: fron xmin - 1 to xmax + 1 included.self.matrix:npt.NDArray[np.bool_]=np.zeros((ymax-ymin+3,xmax-xmin+3),dtype=np.bool_)self.ly,self.lx=self.matrix.shapefor(y,x)inexcavated:y=y-ymin+1x=x-xmin+1self.matrix[y,x]=Truedefdig_pool(self)->None:defneighs(coords:Coords)->Iterator[Coords]:def_neighs(coords:Coords):y,x=coordsyieldy,x-1yieldy,x+1yieldy-1,xyieldy+1,xfory,xin_neighs(coords):if0<=y<self.lyand0<=x<self.lx:yieldy,xoutside=np.zeros_like(self.matrix)visited=np.zeros_like(self.matrix)start=(0,0)outside[start]=Truevisited[start]=Truecurrents:set[Coords]={(0,0)}whilelen(currents)>0:nexts:set[Coords]=set()forpositionincurrents:forneighinneighs(position):ifvisited[neigh]:continueifnotself.matrix[neigh]:outside[neigh]=Truenexts.add(neigh)visited[neigh]=Truecurrents=nextsforposition,_innp.ndenumerate(self.matrix):# type: ignoreifnotoutside[position]:self.matrix[position]=Truedefarea(self)->int:returnnp.sum(self.matrix)# type: ignoredef__str__(self)->str:s=io.StringIO()forlineinself.matrix:forexcavatedinline:ifexcavated:s.write('â')else:s.write(' ')s.write('\n')returns.getvalue()@dataclasses.dataclass(frozen=True)classHSegment:x1:intx2:inty:intdefcuts(self,x:int)->bool:returnself.x1<=x<=self.x2def__len__(self)->int:returnself.x2-self.x1+1@dataclasses.dataclass(frozen=True)classVSegment:x:inty1:inty2:intdefcuts(self,y:int)->bool:returnself.y1<=y<=self.y2def__len__(self)->int:returnself.y2-self.y1+1classTerrain2:def__init__(self,instructions:Iterable[Instruction]):hsegments:set[HSegment]=set()vsegments:set[VSegment]=set()ys:set[int]={0,1}xs:set[int]={0,1}ymin,ymax=0,1xmin,xmax=0,1y,x=0,0forinstructionininstructions:y_,x_=instruction.apply((y,x))ymin,ymax=min(ymin,y_),max(ymax,y_+1)xmin,xmax=min(xmin,x_),max(xmax,x_+1)ys.add(y_)ys.add(y_+1)xs.add(x_)xs.add(x_+1)ifinstruction.directionisDirection.UP:vsegments.add(VSegment(x,y_,y))elifinstruction.directionisDirection.DOWN:vsegments.add(VSegment(x,y,y_))elifinstruction.directionisDirection.LEFT:hsegments.add(HSegment(x_,x,y))elifinstruction.directionisDirection.RIGHT:hsegments.add(HSegment(x,x_,y))else:assertFalse,"we covered all cases for instruction direction"y,x=y_,x_ys.add(ymin-1)ys.add(ymax+1)xs.add(xmin-1)xs.add(xmax+1)self.ys=sorted(ys)self.xs=sorted(xs)self.matrix:npt.NDArray[np.bool_]=np.zeros((len(ys)-1,len(xs)-1),dtype=np.bool_)self.lj,self.li=self.matrix.shapeforhsegmentinhsegments:j=self.ys.index(hsegment.y)foriinrange(self.xs.index(hsegment.x1),self.xs.index(hsegment.x2)+1):self.matrix[j,i]=Trueforvsegmentinvsegments:i=self.xs.index(vsegment.x)forjinrange(self.ys.index(vsegment.y1),self.ys.index(vsegment.y2)+1):self.matrix[j,i]=Truedefdig_pool(self)->None:defneighs(coords:Coords)->Iterator[Coords]:def_neighs(coords:Coords):j,i=coordsyieldj,i-1yieldj,i+1yieldj-1,iyieldj+1,iforj,iin_neighs(coords):if0<=j<self.ljand0<=i<self.li:yieldj,ioutside=np.zeros_like(self.matrix)visited=np.zeros_like(self.matrix)start=(0,0)outside[start]=Truevisited[start]=Truecurrents:set[Coords]={(0,0)}whilelen(currents)>0:nexts:set[Coords]=set()forpositionincurrents:forneighinneighs(position):ifvisited[neigh]:continueifnotself.matrix[neigh]:outside[neigh]=Truenexts.add(neigh)visited[neigh]=Truecurrents=nextsforposition,_innp.ndenumerate(self.matrix):# type: ignoreifnotoutside[position]:self.matrix[position]=Truedefarea(self)->int:count=0forjinrange(self.lj):foriinrange(self.li):ifself.matrix[j,i]:count+=((self.ys[j+1]-self.ys[j])*(self.xs[i+1]-self.xs[i]))returncountdef__str__(self)->str:s=io.StringIO()forlineinself.matrix:forexcavatedinline:ifexcavated:s.write('â')else:s.write(' ')s.write('\n')returns.getvalue()defpart1(lines:aoc.Data)->int:"""Solve puzzle part 1: determine the sum of stuff"""terrain=Terrain1(Instruction.import_line1(line)forlineinlines)terrain.dig_pool()returnterrain.area()defpart2(lines:aoc.Data)->int:"""Solve puzzle part 2: determine the sum of staff"""terrain=Terrain2(Instruction.import_line2(line)forlineinlines)terrain.dig_pool()returnterrain.area()
[^] # Re: Programmation fonctionnelle
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au journal Is return the new goto ?. ĂvaluĂ© Ă 7.
Ă noter que cela permet autre chose d'intĂ©ressant, si le concepteur du langage y a pensĂ© : utiliser cette notion de valeur de retour au niveau, non pas seulement de la fonction, mais plus gĂ©nĂ©ralement du bloc de code. Vous savez, le bloc, une portion de code entre accolades par exemple. Dans les langages oĂč c'est dĂ©fini, ça sert entre autres pour la portĂ©e des variables, mais ça permet aussi de grouper des instructions et de dĂ©finir les structures de contrĂŽle comme travaillant sur des blocs.
Bref, en clair, cette idée de valeur de retour peut permettre des trucs comme ça :
# Programmation fonctionnelle
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au journal Is return the new goto ?. ĂvaluĂ© Ă 10.
Autant que je sache, ça vient d'habitudes de langages de programmation fonctionnelle. J'ai en tĂȘte Caml par exemple. Si je me souviens bien, l'idĂ©e est qu'il n'est pas normal de faire quoi que ce soit qui retourne une valeur sans faire quelque chose de cette valeur : l'affecter Ă une variable par exemple.
Du coup, dans la définition d'une fonction, on s'attend à ce qu'aucune ligne ne laisse de valeur implicitement jetée à la poubelle. Et il devient du coup plus ou moins naturel que, lorsqu'une ligne, une seule, la derniÚre, laisse une valeur, ce soit la valeur de retour de la fonction.
On peut voir les choses autrement. Une fonction est comme l'objet du mĂȘme type en mathĂ©matiques : quelque chose qui prend des valeurs et fournit une valeur. Or en maths, on Ă©crit fort naturellement (ou pas) des choses comme :
f: x ⊠x2. Et certainement pasf: x ⊠return x2.[^] # Re: Broyé du Poitou
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse Ă la dĂ©pĂȘche Claire Mathieu et les algorithmes. ĂvaluĂ© Ă 7.
Sans les ingrédients, j'ai bien peur que ce soit assez ambigu malheureusement.
# Pérennité des formats
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse Ă la dĂ©pĂȘche Archiver ses vidĂ©os : retour dâexpĂ©rience. ĂvaluĂ© Ă 8.
Ăa, c'est un problĂšme rĂ©servĂ© aux utilisateurs de logiciels propriĂ©taires. Ăa peut Ă©ventuellement arriver avec du logiciel libre pour des usages de niche.
Mais pour des trucs comme l'image, la vidĂ©o et le son, dĂšs que tu utilises des formats ouverts un minimum connus, tu peux ĂȘtre certain de pouvoir les relire... tant qu'on aura des ordinateurs et des logiciels.
Avec des formats standardisés, tu as aussi une assez bonne garantie de pouvoir les relire plus tard.
[^] # Re: Pareil !
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au journal OĂč il est question de donnĂ©es personnelles. ĂvaluĂ© Ă 4. DerniĂšre modification le 16 janvier 2024 Ă 13:50.
Ă noter que le fait qu'il y ait une plainte qui aboutisse ne changerait strictement rien pour la suite : chaque cas d'abus nĂ©cessite une plainte pour ĂȘtre sanctionnĂ©.
Je veux dire, si je suis une entreprise prĂȘte Ă abuser des donnĂ©es personnelles de gens, je sais que je cours un risque en le faisant. Le fait qu'il y ait un prĂ©cĂ©dent juridique ou non ne change rien au risque en question, puisque la loi est Ă©crite d'une façon qui ne laisse aucune ambiguĂŻtĂ© : ce que je fais est illĂ©gal et, en cas de procĂšs, sera sans aucun doute considĂ©rĂ© comme tel.
[^] # Re: Pareil !
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au journal OĂč il est question de donnĂ©es personnelles. ĂvaluĂ© Ă 4.
C'est à dire que les boßtes qui pratiquent ce genre d'abus n'en ont juste rien à torcher de la loi, comptant sur le fait que personne ne se plaindra. à ce compte-là , autant déconner à plein tube, pas la peine de se faire chier à respecter quoi que ce soit du RGPD en fait.
[^] # Re: Pareil !
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au journal OĂč il est question de donnĂ©es personnelles. ĂvaluĂ© Ă 8.
Une défense d'une entreprise qui se prémunirait d'un consentement recueilli par case pré-cochée ne tiendrait pas une seconde devant un tribunal, tellement le RGPD est clair. Préambule, paragraphe 32 :
Ce n'est mĂȘme pas une dĂ©duit du texte, c'est Ă©crit dedans de façon on ne peut plus explicite. Le juge est parfois lĂ pour essayer de deviner l'intention du lĂ©gislateur, mais lĂ il n'y a aucune place pour une interprĂ©tation quelconque, c'est juste Ă©crit noir sur blanc : il n'y a pas de consentement par cas prĂ©-cochĂ©e. Fin de la discussion.
[^] # Re: Pareil !
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au journal OĂč il est question de donnĂ©es personnelles. ĂvaluĂ© Ă 10.
C'est moins craignos en effet, mais quand mĂȘme illĂ©gal. Il s'agit d'un traitement de donnĂ©es personnelles effectuĂ© sans consentement.
Ah, au fait, à propos de consentement, il y a un truc important à savoir. Pour qu'un traitement de données personnelles destiné à une prospection commerciale soit légal, il faut un consentement libre, spécifique, éclairé et univoque :
Donc, en particulier, si vous avez comme moi pris l'habitude de ne jamais cocher les cases de consentement au partage de vos donnĂ©es personnelles, vous pouvez ĂȘtre sĂ»r que tout traitement de vos donnĂ©es personnelles par une entreprise avec laquelle vous n'avez jamais Ă©tĂ© en rapport est illĂ©gal puisque vous n'y avez jamais explicitement consenti. Et si vous avez laissĂ© une case prĂ©-cochĂ©e, aucune importance, ces saloperies ne valent pas consentement.
# Pareil !
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au journal OĂč il est question de donnĂ©es personnelles. ĂvaluĂ© Ă 10.
C'est amusant, il m'est arrivĂ© presque la mĂȘme chose, en France, Ă peu prĂšs au mĂȘme moment. Merci d'en avoir fait un journal d'ailleurs.
Dans mon cas, c'était une lettre m'annonçant l'ouverture d'un genre de centre commercial multi-marques à Giverny. Un truc donc je n'ai strictement rien à cirer, c'est à une heure de bagnole de chez moi et j'ai mieux à faire de mon temps que de rouler pour aller passer une journée à faire des courses, merci.
Bref, comme c'Ă©tait du courrier adressĂ©, je les ai aussitĂŽt contactĂ©s par courrier Ă©lectronique pour leur demander l'intĂ©gralitĂ© des donnĂ©es personnelles qu'ils avaient sur moi et d'oĂč ils les sortaient. Ă noter que c'est tout ce que je leur demandais, en particulier, je ne leur ai Ă ce moment-lĂ pas du tout demandĂ© de supprimer quoi que ce soit.
Réponse : ça vient d'un partenaire. Point. Pas plus de détail. Ils n'avaient manifestement pas compris ou pas voulu comprendre ma demande, donc je leur réponds en leur rappelant que je leur demande communication de ces données et que je veux savoir de quel partenaire il s'agit, avec adresse et numéro SIRET ou enregistrement au RCS, merci.
Pas de rĂ©ponse en quinze jours. Je dĂ©cide de les dĂ©noncer Ă la Cnil, et je les en informe au passage. Et lĂ , ils rĂ©pondent â mais trop tard, la Cnil est dĂ©jĂ au courant qu'ils dĂ©connent â en m'expliquant qu'ils n'ont pas de donnĂ©es sur moi, qu'ils ont juste demandĂ© Ă Mediapost de faire une campagne de publicitĂ© pour leur compte. C'est donc Mediapost qui a des donnĂ©es sur moi, et ils leur transmettent ma demande de suppression. Ma demande de suppression, quelle demande de suppression ✠Je n'ai jamais rien demandĂ© de tel moi !
Bref. Pour info, c'est comme dans ton cas, parce que Mediapost c'est La Poste française. Qui évidemment ont moyen d'avoir des infos sur à peu prÚs tout le monde. Et n'ont absolument pas le droit de les collecter ou pire, de les utiliser comme ça.
Une autre chose que je remarque presque tout le temps, lorsqu'on écrit à une entreprise pour leur demander les données personnelles relatives à un envoi publicitaire, s'ils répondent, c'est presque tout le temps pour indiquer :
[^] # Re: Fromagerie
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au journal [ HS ] Fromage rĂąpĂ© pour accompagner les pĂątes ou autre .... ĂvaluĂ© Ă 3.
Et pour le prix au kilo d'un emmental français rĂąpĂ©, on peut se payer un truc nettement plus qualitatif. Peut-ĂȘtre pas du ComtĂ©, mais au moins un Truc de Savoie AOP.
[^] # Re: Géométrie vectorielle et analytique
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au message Advent of Code 2023, jour 24. ĂvaluĂ© Ă 3.
Notes d'implémentation :
fractions.Fraction.Vectoret des opérateurs variés. En Python, on peut par exemple définir des méthodes qui réutilisent les opérateurs standard de façon habituelle ou futée (vous allez comprendre...) :__rmul__pour le produit par un scalairealpha * v;__xor__pour le produit vectorielv ^ w;__add__et__sub__pour l'addition et la soustraction vectorielles ;__neg__pour l'opposition ;__floordiv__pour le test de colinéaritév // w;__bool__pour le test de non-nullité (permet d'écrire des trucs commeif vector).//,==, etc.[^] # Re: Géométrie vectorielle et analytique
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au message Advent of Code 2023, jour 24. ĂvaluĂ© Ă 3.
Pour déterminer la position de départ donc, la premiÚre idée consiste à chercher l'intersection de deux trajectoires corrigées. Une intersection de droites donc. Sauf que c'est assez moche à calculer, on peut faire plus élégant.
Les trajectoires corrigĂ©es Ă©tant toutes sĂ©cantes au mĂȘme point, celui qu'on recherche, en les prenant deux par deux on peut dĂ©finir des plans. Il est temps d'ouvrir une petite parenthĂšse.
Plan défini par deux droites
Dans l'espace, deux droites peuvent définir un plan, à condition nécessaire et suffisante qu'elles soient soit parallÚles mais non identiques, soit sécantes.
Soient deux droites pertinentes selon cette condition et définies chacune par un point et par un vecteur
(p0, v0)et(p1, v1). On cherche à caractériser leur plan commun, par un point et un vecteur normal.Si elles ne sont pas parallÚles, leurs vecteurs directeurs
v0etv1ne sont pas colinéaires. Leur produit vectorieln = v0 ^ v1est non nul et orthogonal aux deux droites et fait donc un trÚs bon vecteur normal pour le plan cherché.Si elles sont parallÚles leurs vecteurs directeurs sont certes colinéraires, mais
p0 - p1n'est pas colinéaire à ces derniers (sinon, vous pouvez vérifier, les droites seraient identiques). On peut donc choisirn = v0 ^ (p0 - p1)comme vecteur normal pour le plan cherché.Il nous reste à trouver un point sur le plan cherché : c'est trivial, il suffit de prendre par exemple
p0puisque ce plan contient par dĂ©finition la premiĂšre droite. Pour obtenir une Ă©quation de plan du typen â r = d, il suffit de prendred = p0 â n.En fait, nous nous intĂ©ressons seulement au cas de droites sĂ©cantes, voici donc le calcul pour obtenir l'Ă©quation du plan dans ce cas spĂ©cifique :
Retour au problĂšme
En prenant les trajectoires corrigées deux par deux, on peut définir un tas de plans contenant tous le point cherché. Or l'intersection de trois plans non parallÚles est un point, qui sera forcément ce dernier ! Ouvrons une nouvelle parenthÚse.
Intersection de plans
Allons-y pour l'intersection de trois plan non parallĂšles
(p0, d0),(p1, d1)et(p2, d2).Le point d'intersection, que nous allons noter
r, respecte les équation des trois plans :L'astuce consiste ici à choisir une base vectorielle alternative :
En exprimant la position cherchée comme combinaison de ces trois vecteurs
r = a0 u 0 + a1 u1 + a2 u2, l'équation du premier plan devient :On peut reconnaßtre ici le produit mixte de nos trois vecteurs normaux, un scalaire que l'on va noter
U = n0 â (n1 ^ n2). Les trois Ă©quations deviennent de la mĂȘme façon :Ce qui nous donne finalement ces fameux coefficients :
Qui nous donnent donc le vecteur position de l'intersection de nos trois plans. Pour rappel, voici les calculs à effectuer à partir des constantes définissant les trois plans :
Re-retour au problĂšme
En prenant trois trajectoires corrigées
(p0, v0 - v),(p1, v1 - v)et(p2, v2 - v), on sait désormais caractériser leurs plans communs deux à deux, soit trois plan. Et on sait également déterminer l'intersection de ces trois plan. ProblÚme résolu !# Géométrie vectorielle et analytique
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au message Advent of Code 2023, jour 24. ĂvaluĂ© Ă 3.
Sommaire
Bon, c'est un problÚme de géométrie.
Je passe sur la premiĂšre partie, trouver des intersections de ligne dans le plan c'est sans grand intĂ©rĂȘt.
La seconde partie est beaucoup, beaucoup plus intéressante. J'ai vainement cherché une solution élégante avant d'en trouver une sur Reddit. Je vous l'explique.
Ă supposer que l'on trouve une position de dĂ©part p et une vitesse v qui permette de percuter tous les grĂȘlons, en se plaçant dans le rĂ©fĂ©rentiel de notre projectile, ce sont tous les grĂȘlons qui vont converger jusqu'Ă l'atteindre, chacun Ă son tour. Autrement dit, pour un grĂȘlon
(p0, v0), la droite(p0, v0 - v)passe par notre position de dĂ©part p. Et il en est de mĂȘme pour les autres grĂȘlons.Par consĂ©quent, les trajectoires corrigĂ©es des trois premiers grĂȘlons
(p0, v0 -v), (p1, v1 -v)et(p2, v2 -v)se coupent deux à deux en p. Il est temps d'ouvrir une parenthÚse sur les droites sécantes.Des droites sécantes
Dans l'espace, contrairement au plan, des droites peuvent ĂȘtre identiques, parallĂšles, sĂ©cantes ou... rien de tout ça.
Ătant donnĂ©es deux droites caractĂ©risĂ©es chacune par un point et un vecteur directeur
l0 = (p0, v0)etl1 = (p1, v1), la premiĂšre chose Ă vĂ©rifier est qu'elles ne sont ni identiques ni parallĂšles. Autrement dit, que leurs vecteurs directeurs ne sont pas colinĂ©aires. Une façon de faire consiste Ă prendre leur produit vectoriel, qui ne doit pas ĂȘtre nul.Si ces droites ne sont pas parallĂšles, les droites vectorielles associĂ©es
(O, v0)et(O, v1), dĂ©finissent un plan vectoriel. Ce plan vectoriel peut ĂȘtre caractĂ©risĂ© par un vecteur normal facile Ă construire par le produit vectoriel des vecteurs directeurs de ces deux droites :n = v0 ^ v1.Le plan affine parallĂšle Ă ce plan vectoriel et incluant
d0a pour Ă©quation vectorieller â n = p0 â n. Le plan affine incluantd1a quant a lui pour Ă©quation vectorieller â n = p1 â n.Ces plans sont identiques si et seulement si
p0 â n = p1 â n. Autrement dit, en revenant Ă la dĂ©finition de ce vecteur normal n, si(p0 - p1) â (v0 ^ v1) = 0. Si ces plans sont identiques, les droites sont coplanaire, et n'Ă©tant pas parallĂšles, elles sont donc sĂ©cantes. RĂ©ciproquement, si les droites sont coplanaires, les plans sont identiques.Retour au problĂšme
Puisque le problĂšme a une solution, les trajectoires corrigĂ©es de deux grĂȘlons
(p0, v0 - v)et(p1, v1 - v)sont sĂ©cantes. Bon, ok, Ă condition qu'elles ne soient pas identiques : si c'Ă©tait le cas on prendrait juste une autre grĂȘlon, on en a plein Ă notre disposition. Mais vous pouvez vĂ©rifier sur vos donnĂ©es, ce cas ne se prĂ©sente pas. Par consĂ©quent :En rĂ©organisant un peu et en utilisant les propriĂ©tĂ© du produit mixte :
Donnons un nom Ă ces termes constants :
On a donc :
Plus de droites
De la mĂȘme façon, en utilisant une droite de plus, posons :
On a maintenant trois équations sur v :
Pour résoudre ça simplement, il y a une astuce. On va utiliser une base vectorielle ainsi définie, avec des vecteurs conçus que chacun d'entre eux soit orthogonal à deux vecteurs des équations précédentes :
Et écrire la vitesse que nous cherchons sur cette base :
v = a0 u0 + a1 u1 + a2 u2. Les équations précédentes deviennent désormais :On peut reconnaßtre ici le produit mixte de nos vecteurs
A0et compagnie, que l'on va nommerA* = A0 â (A1 ^ A2), ce qui donne :Et finalement nos coefficients :
Récapitulons
Avec les trois premiers grĂȘlons, on calcule les vecteurs et scalaires constants suivants :
Puis les vecteurs suivants :
Et enfin les coefficients suivants :
La vitesse de notre projectile doit ĂȘtre :
Et la position alors ?
La vitesse, c'est bien, mais c'est surtout la position de dĂ©part qu'on nous demande. On va dire que c'est facile Ă dĂ©duire dĂ©sormais : c'est l'intersection des trajectoires corrigĂ©es des deux premiers grĂȘlons. Par exemple, on peut en prendre d'autres si on veut.
Sauf que non, ce n'est vraiment pas trivial à calculer, l'intersection de deux droites sécantes dans l'espace. Il y a encore une astuce, et celle-là est vraiment de moi. Je vous la donnerai plus tard, là je suis fatigué de raconter mes aventures mathématiques.
[^] # Re: J'ai aussi utilisé le Z3 theorem prover.
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au message Advent of Code 2023, jour 24. ĂvaluĂ© Ă 3.
Un code de cette longueur, ça aurait valu la peine de le mettre ailleurs, là ça nous fait une page terriblement longue à parcourir.
# Fromagerie
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au journal [ HS ] Fromage rĂąpĂ© pour accompagner les pĂątes ou autre .... ĂvaluĂ© Ă 4.
Du fromage acheté chez un producteur à l'occasion de vacances à la montagne. Ou du fromage acheté à la fromagerie de mon quartier. Ou au marché. Mais en aucun cas du fromage industriel pré-rùpé.
Bref, du fromage en morceau que je rĂąpe moi-mĂȘme... ou que je demande au fromager de rĂąper et de mettre dans une gamelle qui m'appartient. De l'achat en vrac quoi.
# Pas d'exemple
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au message Advent of Code 2023, jour 20. ĂvaluĂ© Ă 3.
à noter que les exemples fournis en premiÚre partie ne s'appliquaient pas à la deuxiÚme partie, et que cette derniÚre ne fournissait aucun exemple utilisable. J'ai trouvé ça vache.
[^] # Re: La solution la plus courte à écrire, la plus longue à expliquer.
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au message Advent of Code, jour 18. ĂvaluĂ© Ă 3.
Non mais lĂ , faut savoir s'arrĂȘter quand mĂȘme. Ce serait dommage de mourir ou de tuer quelqu'un pour cause d'AoC.
[^] # Re: Optimisation insuffisante
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au message Advent of Code, jour 19. ĂvaluĂ© Ă 3.
Bon, j'ai eu mon résultat en un peu moins d'une heure avec PyPy. Mais ça reste moche à mes yeux. à l'occasion, je m'essaierai à optimiser encore ça avec un découpage plus astucieux.
[^] # Re: Solution en Haskell.
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au message Advent of Code, jour 19. ĂvaluĂ© Ă 3.
Ah, je crois que je devine la subtilité. En descendant la chaßne des workflows, ou plutÎt la chaßne des rÚgles et des workflows, on peut découper sur chaque test, ce qui fait au final beaucoup moins de pavés qu'en quadrillant l'espace entier.
[^] # Re: Solution en Haskell.
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au message Advent of Code, jour 19. ĂvaluĂ© Ă 3.
Est-ce que tu découpes donc tout l'espace autour de chaque plan correspondant aux valeurs des seuils des critÚres des workflows ?
[^] # Re: Solution en Haskell.
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au message Advent of Code, jour 19. ĂvaluĂ© Ă 3.
Je ne suis pas sûr de comprendre, et ne lisant pas la Haskell, je me demande si tu peux m'éclairer. Est-ce un genre de dichotomie que tu fais ? Sur quel critÚre découpes-tu ou non un pavé de valeurs ?
# Optimisation insuffisante
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au message Advent of Code, jour 19. ĂvaluĂ© Ă 3.
La premiĂšre partie ne pose pas de problĂšme particulier. La seconde en revanche, c'est une autre paire de manches.
Mon idée pour optimiser cela consiste à réduire le nombre de cas que l'on teste. En effet, les workflows ont des rÚgles basées sur des seuils, et fondamentalement, il suffit de balayer l'ensemble des combinaisons de ces valeurs seuils pour pouvoir déterminer ce qui arrivera à l'importe quelle combinaison entre ces valeurs.
Il faut faire attention à la façon dont on définit les seuils en question, parce que les définitions utilisent les opérateurs < et > qui ne sont pas l'opposé l'un de l'autre. Bref, il y a un détail important que je ne donnerai pas ici.
Toujours est-il que, pour mes donnĂ©es, ça me ramĂšne Ă 6,4 Ăă°ă€ 109 = 6,4 milliards de possibilitĂ©s. Et c'est un peu long Ă tester : Ă un rythme de l'ordre de 150.000 combinaisons par seconde, ça va me prendre une douzaine d'heures. Il faut que je trouve mieux que ça...
# Trois dimensions
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au message Advent of Code, jour 17. ĂvaluĂ© Ă 3. DerniĂšre modification le 18 dĂ©cembre 2023 Ă 20:15.
Pour ce problÚme, j'ai considéré qu'on n'était pas vraiment en deux dimensions, mais plutÎt en trois. Parce que l'état d'un creuset, ce n'est pas seulement sa position sur le terrain, mais aussi le nombre de cases qu'il a parcouru dans une direction donnée... ce qui se représente également trÚs bien par un nombre, que je considÚre comme une troisiÚme coordonnée.
Ăa permet de se ramener Ă un problĂšme de parcours de proche en proche, avec une dĂ©finition bien particuliĂšre des voisins d'un point.
Voici le code :
[^] # Re: Sans géométrie
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au message Advent of Code, jour 18. ĂvaluĂ© Ă 3. DerniĂšre modification le 18 dĂ©cembre 2023 Ă 20:01.
Le code :
# Sans géométrie
PostĂ© par đČ Tanguy Ortolo (site web personnel) . En rĂ©ponse au message Advent of Code, jour 18. ĂvaluĂ© Ă 3.
PremiĂšre partie
Pour la premiÚre partie, je fais du remplissage, voici les étapes :
Trouver l'aire, c'est trivial.
DeuxiĂšme partie
Pareil. Comment ça, pareil, alors que les coordonnĂ©es sont beaucoup trop grandes pour pouvoir faire ça avec des matrices ? Eh bien, on n'a pas besoin de toutes les coordonnĂ©es, voyez-vous. On a seulement besoin de celles oĂč commencent ou finissent des tranchĂ©es en fait.
Bref, je me construis une matrice donc les cases ont des dimensions virtuelles carrément variables. Ensuite, on se ramÚne à l'algorithme précédent, avec un détail de pondération pour le calcul de l'aire.