Comptez les carrés

18

Défi

L'origami (papier pliant) est une forme d'art créative. Pour autant que je sache, le maître d'Origami préfère le papier carré. Commençons par le début - convertissons un papier rectangulaire en papier carré.

Le papier est donc divisé en carrés. Nous supprimons le plus grand carré qui partage un bord plus court avec la forme actuelle, étape par étape (voir l'image ci-dessous). Et si la partie restante après une étape est inférieure ou égale à 0.001 * (area of the original paper), le papier ne peut plus être divisé. Il est possible que rien ne reste enfin.

Votre tâche consiste à calculer le nombre de carrés créés au cours du processus. Le carré de la dernière étape qui empêche le papier d'être divisé est compté dans la sortie.

Exemple (un papier de 1.350largeur / hauteur), la sortie est 10:

exemple de tranche

Entrée et sortie

Entrée: rapport largeur / hauteur pour le papier rectangulaire, une décimale (ou un entier sans le point) de 1.002à 1.999avec un pas minimal de 0.001. Vous pouvez également utiliser tout autre format raisonnable décrivant le rapport. Mentionnez-le simplement dans votre réponse.

Sortie: nombre carré, un entier.

Exemple d'E / S

Un format de mappage est utilisé pour garder la page bien rangée, tandis que votre code n'a pas besoin de prendre en charge une entrée de liste ni d'être une fonction de mappage.

1.002 => 251
1.003 => 223
1.004 => 189
1.005 => 161
1.006 => 140
1.007 => 124
1.008 => 111
1.009 => 100

Liste de toutes les réponses

Grâce à @LuisMendo, voici le graphique des réponses.

graphique

Remarques

  • Ceci est un code-golf donc le code le plus court gagne
  • Faites attention aux failles standard
  • C'est à vous de décider comment gérer les entrées et les sorties, mais elles doivent respecter les restrictions standard.

Au fait...

  • Commentez si vous avez quelque chose de peu clair sur le défi
  • Personnellement, je dirais que votre réponse contient une explication si vous utilisez une langue de golf
  • Merci à @GregMartin, lisez sa réponse pour une bonne explication mathématique du défi.

Exemple de code

Voici une version non golfée du code C ++:

#include <iostream>
#include <utility>

int f (double m)
{
    double n = 1, k = 0.001;
    int cnt = 0;
    k *= m;                       // the target minimum size
    while(m*n >= k)
    {
        m -= n;                   // extract a square
        if(n > m)
            std::swap(n, m);      // keep m > n
        ++ cnt;
    }
    return cnt;
}

int main()
{
    double p;
    std::cin >> p;
    std::cout << f(p);
    return 0;
}

Tous les calculs liés à l'exemple de code nécessitent une précision de 6 chiffres décimaux, qui est couverte par float.

Keyu Gan
la source
Peut-on utiliser deux nombres formant le rapport comme entrées?
Luis Mendo
@LuisMendo oui, selon votre souhait.
Keyu Gan
2
Beau défi!
flawr
5
La liste des réponses produit un joli graphique
Luis Mendo
1
@KeyuGan Bien sûr, allez-y! Faites-moi savoir si vous avez besoin d'une version avec un autre format
Luis Mendo

Réponses:

2

MATL , 19 octets

`SZ}y-htG/p1e-3>}x@

L'entrée est un tableau avec les deux nombres définissant le rapport d'origine, tels que [1, 1.009]. (Il n'est pas nécessaire que les numéros soient triés ou que l'un d'eux soit 1.)

Essayez-le en ligne!

Explication

`        % Do...while loop
  S      %   Sort array. Takes 1×2 array as input (implicit) the first time
  Z}     %   Split array into its 2 elements: first the minimum m, then the maximum M
  y      %   Duplicate m onto the top of the stack. The stack now contains m, M, m
  -      %   Subtract. The stack now contains m, M-m
  h      %   Concatenate into [m, M-m]. This is the remaining piece of paper
  t      %   Duplicate
  G/     %   Divide by input, element-wise
  p      %   Product of array. Gives ratio of current piece's area to initial area
  1e-3>  %   True if this ratio exceeds 1e-3. In that case the loop continues
}        % Finally (execute after last iteration, but still within the loop)
  x      %   Delete last piece of paper
  @      %   Push current loop counter. This is the result
         % End (implicit)
         % Display (implicit)
Luis Mendo
la source
6

Haskell , 71 70 65 63 62 61 58 56 octets

Merci à @xnor pour quelques améliorations ingénieuses!

(n#m)e|e>n*m*1e3=0|n<m=m#n$e|d<-n-m=(d#m)e+1
n!m=n#m$n*m

Essayez-le en ligne!

flawr
la source
Il pense que la m==nfin peut être 1>0parce que c'est la seule possibilité qui reste. Ou, peut-être que les cas pourraient être réorganisés pour permettre une liaison ici.
xnor
En fait, le dossier de l'égalité est-il nécessaire? Si n>mest étendu à n>=met que la première vérification est écrite e>m*n*1000, cela devrait donner l' 1égalité.
xnor
@xnor Bonne idée, merci!
flawr
1
Déplacement des gardes pour 56:(n#m)e|e>n*m*1e3=0|n<m=m#n$e|d<-n-m=(d#m)e+1;n!m=n#m$n*m
xnor
Wow, utiliser le d<-n-mcomme otherwisec'est vraiment bien !!!
flawr
4

JavaScript (ES6), 59 58 octets

f=(m,n=!(k=m/1e3,c=0))=>m*n<k?c:(c++,m-=n)<n?f(n,m):f(m,n)

Tester

Arnauld
la source
4

Mathematica, non-non concurrentiel (21 octets)

Cette réponse n'est pas concurrente car elle ne répond pas à la vraie question posée! Mais il répond à une variante de la question et fournit une excuse pour mettre en évidence des mathématiques intéressantes.

Tr@*ContinuedFraction

Fonction symbolique prenant en entrée un nombre rationnel positif (dont le numérateur et le dénominateur représentent les dimensions du papier d'origine) et renvoyant un entier positif. Par exemple, Tr@*ContinuedFraction[1350/1000]renvoie 10. ( ContinuedFractionagit différemment sur les nombres à virgule flottante en raison de problèmes de précision, c'est pourquoi un nombre rationnel est nécessaire en entrée dans ce contexte.)

Une interprétation intéressante de la procédure géométrique décrite dans le problème (couper des carrés d'un rectangle à plusieurs reprises) est qu'il s'agit d'une implémentation de l'algorithme euclidien pour trouver les plus grands diviseurs communs! Considérez l'exemple dans la question elle-même, avec un ratio1.35, qui pourrait être modélisé en ayant un morceau de papier avec des dimensions (1350,1000). Chaque fois qu'un carré est coupé, le plus petit nombre est soustrait du plus grand nombre; donc les rectangles résultants dans cet exemple ont des dimensions (350,1000), puis (350,650), puis (350,300), puis (50,300), puis (50,250) et (50,200) et (50,150) et (50,100) et (50, 50), et aussi (50,0) une fois que nous avons retiré le dernier carré de lui-même. C'est exactement ainsi que fonctionne l'algorithme euclidien (modulo la différence entre division et soustraction répétée), et en effet on voit que 50 est bien le GCD de 1350 et 1000.

Typiquement dans l'algorithme euclidien, on garde une trace de ces dimensions intermédiaires et rejette le nombre de soustractions; cependant, on peut alternativement enregistrer combien de fois nous avons soustrait un nombre de l'autre avant que la différence ne devienne trop petite et nous devons changer ce que nous soustrayons. Cette méthode d'enregistrement est précisément la fraction continue d'un nombre rationnel. (Les fractions continues de nombres irrationnels, qui ne se terminent jamais, sont également super cool, mais pas pertinentes ici.) Par exemple, dans l'exemple 1350/1000, nous avons soustrait 1000 1fois, puis 350 2fois, puis 300 1fois, puis 50 6fois; par conséquent, la fraction continue de 1350/1000 est {1,2,1,6}. Mathématiquement, nous avons réécrit 1350/1000 en 1+ 1 / ( 2+ 1 / ( 1+ 1 /6)), que vous pouvez vérifier.

Donc, pour ce problème, si vous ne vous arrêtez pas lorsque les carrés deviennent plus petits qu'un certain seuil, mais comptez simplement tous les nombres finis de carrés avant qu'il ne s'arrête, alors le nombre total de carrés est égal au nombre total de soustractions, c'est-à-dire la somme de tous les entiers de la fraction continue - et c'est précisément ce que Tr@*ContinuedFractioncalcule la composition des fonctions ! (Pour l'exemple donné 1.35, il obtient la réponse que désire l'OP, car le carré final est suffisamment grand pour que tous les carrés soient comptés. Mais Tr@*ContinuedFraction[1001/1000], par exemple, donne 1001, car il compte le grand carré et les 1000 petits carrés 1x1000. .)

Greg Martin
la source
1
Bien que cela soit effectivement intéressant, le label non concurrent est réservé aux langues plus récentes que le challenge . Indépendamment de cela, toutes les réponses doivent résoudre le défi. Par conséquent, cette réponse devrait vraiment être supprimée. Seriez-vous en mesure de reconstruire à partir de la liste des fractions continues où le couper afin que cela puisse être transformé en une solution tout aussi intéressante mais valide?
Martin Ender
1
J'ai eu une démangeaison mentale à gratter lors de l'écriture de cette réponse, mais je conviens que c'est une réponse digne de suppression selon les normes de la communauté. (Merci pour vos commentaires doux mais précis!) Si TPTB a envie de retarder sa suppression de 24 heures, je pourrais être en mesure de compliquer l'approche pour donner la bonne réponse ... sinon, supprimez-le et pas de rancune.
Greg Martin
3

Mathematica, 64 53 octets

({t=1,#}//.{a_,b_}/;1000a*b>#:>Sort@{++t;a,b-a};t-1)&

Une solution impérative (style C) a exactement la même longueur:

(For[t=a=1;b=#,1000a*b>#,If[a>b,a-=b,b-=a];++t];t-1)&
Martin Ender
la source
2

C (GCC / Clang), 61 59 octets

c,k;f(m,n){for(k=m*n;m*n/k;m>n?(m-=n):(n-=m))++c;return c;}

L'entrée est deux entiers (largeur et hauteur) sans point, tels que f(1999,1000).

J'espère que quelqu'un pourrait économiser un octet en poussant C dans le club de 58 octets. ;)

Keyu Gan
la source
Suggérer de supprimer les parenthèses autourm-=n
plafondcat
1

C, 59 octets

s,a,n=1e3;C(m){for(a=m;m*n>a;s++)m>n?m-=n:(n-=m);return s;}

Essayez-le en ligne

L'entrée est un entier qui est le rapport largeur / hauteur en millièmes (par exemple 1002 pour 1,002: 1).

Version non golfée

int C(int m)
{
    int n = 1000;
    int a = m;
    int s = 0;

    while (m * n > a)
    {
        if (m > n)
            m -= n;
        else
            n -= m;

        s++;
    }

    return s;
}
bmann
la source