Problème de Josephus (en comptant)


29

Le défi

Écrivez une fonction qui prend deux entiers positifs n et k comme arguments et retourne le numéro de la dernière personne restante sur n après avoir compté chaque k -ième personne.

C'est un défi de code-golf, donc le code le plus court l'emporte.

Le problème

n personnes (numérotées de 1 à n ) sont debout dans un cercle et chaque k - ième est compté jusqu'à ce qu'une seule personne est disponible (voir le correspondant article wikipedia ). Déterminez le nombre de cette dernière personne.

Par exemple, pour k = 3, deux personnes seront ignorées et la troisième sera décomptée. C'est-à-dire que pour n = 7, les nombres seront comptés dans l'ordre 3 6 2 7 5 1 (en détail 1 2 3 4 5 6 7 1 2 4 5 7 1 4 5 1 4 1 4 ) et donc la réponse est 4 .

Exemples

J(7,1) = 7      // people are counted out in order 1 2 3 4 5 6 [7]
J(7,2) = 7      // people are counted out in order 2 4 6 1 5 3 [7]
J(7,3) = 4      // see above
J(7,11) = 1
J(77,8) = 1
J(123,12) = 21

Réponses:


5

GolfScript, 17 octets

{{@+\)%}+\,*)}:f;

Prend n kla pile et laisse le résultat sur la pile.

Dissection

Cela utilise la récurrence g(n,k) = (g(n-1,k) + k) % navec g(1, k) = 0(comme décrit dans l'article Wikipedia) avec la récursivité remplacée par un pli.

{          # Function declaration
           # Stack: n k
  {        # Stack: g(i-1,k) i-1 k
    @+\)%  # Stack: g(i,k)
  }+       # Add, giving stack: n {k block}
  \,*      # Fold {k block} over [0 1 ... n-1]
  )        # Increment to move from 0-based to 1-based indexing
}:f;

Pouvez-vous ajouter une explication, s'il vous plaît?
Sherlock9

@ Sherlock9, j'ai réussi à comprendre ce que je faisais malgré près de 3,5 ans. Qui a dit que GolfScript était en lecture seule? ;)
Peter Taylor

Ahem. s / lire / écrire /
Peter Taylor

Désolé. J'ai seulement commencé à apprendre Golfscript il y a deux ou trois jours et chaque fois que je lisais votre code, je pensais que j'avais raté quelque chose. ... Ok, je manque encore quelque chose, comment ne repliant {k block}sur [0..n-1]vous obtenir g(0,k) 0 kpour commencer? Désolé, si je poste ces questions au mauvais endroit.
Sherlock9

@ Sherlock9, fold fonctionne par paire, donc la première chose qu'il fait est d'évaluer 0 1 block. Très commodément, cela se trouve être g(1, k) (2-1) block. Donc ça commence g(1,k) 1plutôt que g(0,k) 0. Ensuite, après avoir exécuté le bloc, il pousse l'élément suivant du tableau ( 2) et exécute à nouveau le bloc, etc.
Peter Taylor

14

Minsky Register Machine (25 états sans interruption)

Pas techniquement une fonction, mais c'est dans un paradigme informatique qui n'a pas de fonctions en soi ...

Il s'agit d'une légère variation par rapport au scénario de test principal de mon premier défi d'interprétation MRM : Problème de Josephus en tant que machine d'enregistrement Minsky

Entrée dans les registres net k; sortie dans le registre r; on suppose que r=i=t=0lors de l'entrée. Les deux premières instructions d'arrêt sont des cas d'erreur.


Je pense que vous devez ajuster légèrement votre machine. Si je le lis correctement, la sortie est indexée à zéro, n'est-ce pas?
Howard

Je pensais dans l'autre sens: si k=1alors r=0. Hmm, je dois y penser à nouveau ...
Howard

En lisant votre diagramme, il isuffit de compter de 2à ntandis que se rtrouve le registre qui accumule le résultat.
Howard

@Howard, j'ai recherché les commentaires que j'ai faits quand j'ai écrit ceci et vous avez raison. Oups. Maintenant corrigé (je crois - testera plus en profondeur plus tard).
Peter Taylor

7

Python, 36

J'ai également utilisé la formule de wikipedia:

J=lambda n,k:n<2or(J(n-1,k)+k-1)%n+1

Exemples:

>>> J(7,3)
4
>>> J(77,8)
1
>>> J(123,12)
21

6

Mathematica, 38 36 octets

Même formule Wikipédia:

1~f~_=1
n_~f~k_:=Mod[f[n-1,k]+k,n,1]

1
If[#<2,1,Mod[#0[#-1,#2]+#2,#,1]]&
alephalpha

5

C, 40 caractères

C'est à peu près la formule que l'article wikipedia lié ci-dessus donne:

j(n,k){return n>1?(j(n-1,k)+k-1)%n+1:1;}

Pour plus de variété, voici une implémentation qui exécute réellement la simulation (99 caractères):

j(n,k,c,i,r){char o[999];memset(o,1,n);for(c=k,i=0;r;++i)(c-=o[i%=n])||(o[i]=!r--,c=k);
return i;}

4
Enregistrer un caractère: j(n,k){return--n?(j(n,k)+k-1)%-~n+1:1;}.
ugoren

5

dc, 27 octets

[d1-d1<glk+r%1+]dsg?1-skrxp

Utilise la récurrence de l'article Wikipedia. Explication:

# comment shows what is on the stack and any other effect the instructions have
[   # n
d   # n, n
1-  # n-1, n
d   # n-1, n-1, n
1<g # g(n-1), n ; g is executed only if n > 1, conveniently g(1) = 1
lk+ # g(n-1)+(k-1), n; remember, k register holds k-1
r%  # g(n-1)+(k-1) mod n
1+  # (g(n-1)+(k-1) mod n)+1
]
dsg # code for g; code also stored in g
?   # read user input => k, n, code for g
1-  # k-1, n, code for g
sk  # n, code for g; k-1 stored in register k
r   # code for g, n
x   # g(n)
p   # prints g(n)

4

J, 45 caractères

j=.[:{.@{:]([:}.]|.~<:@[|~#@])^:(<:@#)>:@i.@[

Exécute la simulation.

Alternativement, en utilisant la formule (31 caractères):

j=.1:`(1+[|1-~]+<:@[$:])@.(1<[)

J'espère que cela ne dérange pas Howard d'avoir ajusté légèrement le format d'entrée pour l'adapter à un verbe dyadique en J.

Usage:

   7 j 3
4
   123 j 12
21

4

GolfScript, 32 24 octets

:k;0:^;(,{))^k+\%:^;}/^)

Utilisation: attend les deux paramètres n et k dans la pile et laisse la valeur de sortie.

(merci à Peter Taylor d'avoir suggéré une approche itérative et de nombreux autres conseils)

L'ancienne approche (récursive) de 32 caractères:

{1$1>{1$(1$^1$(+2$%)}1if@@;;}:^;

Ceci est mon premier GolfScript, alors faites-moi part de vos critiques.


1
1-a un opcode spécial (. De même 1+est ). Vous n'avez pas à utiliser de caractères alphabétiques pour le stockage, vous pouvez donc utiliser par exemple ^au lieu de Jet pas besoin d'espace après. Vous avez beaucoup plus de $s que d'habitude dans un programme bien joué: demandez-vous si vous pouvez les réduire en utilisant une combinaison de \@..
Peter Taylor

@PeterTaylor Merci beaucoup pour ces bons conseils! Il est assez difficile de saisir tous les opérateurs Golfscript et j'ai négligé ces deux très simples. Ce n'est qu'en appliquant les deux premières suggestions que je parviens à raccourcir le code de 5 caractères. J'essaierai également de supprimer les $références.
Cristian Lupascu

1
De plus, la récursivité n'est pas vraiment l'affaire de GolfScript. Essayez de le retourner et de faire une boucle. Je peux le faire descendre à 19 caractères (bien que le code non testé) de cette façon. Astuce: déroulez la fonction gde l'article Wikipedia, et utilisez ,et /.
Peter Taylor

1
{,{\2$+\)%}*)\;}:f;Assurez-vous de comprendre pourquoi cela fonctionne;)
Peter Taylor

1
Une dernière astuce: plutôt que d'utiliser 2 caractères pour accéder kà l'intérieur de la boucle, puis 2 autres pour la supprimer à la fin, nous pouvons le tirer à l'intérieur en utilisant +pour descendre à 17 caractères: {{@+\)%}+\,*)}:f;je doute que cela puisse être amélioré.
Peter Taylor



2

Haskell, 68

j n=q$cycle[0..n]
q l@(i:h:_)k|h/=i=q(drop(k-1)$filter(/=i)l)k|1>0=i

Fait la simulation réelle. Manifestation:

GHCi> j 7 1
7
GHCi> j 7 2
7
GHCi> j 7 3
4
GHCi> j 7 11
1
GHCi> j 77 8
1
GHCi> j 123 12
21



1

C, 88 caractères

Fait la simulation, ne calcule pas la formule.
Beaucoup plus longue que la formule, mais plus courte que l'autre simulation C.

j(n,k){
    int i=0,c=k,r=n,*p=calloc(n,8);
    for(;p[i=i%n+1]||--c?1:--r?p[i]=c=k:0;);
    return i;
}

Remarques:
1. Alloue de la mémoire et ne libère jamais.
2. Alloue n*8au lieu de n*4, car j'utilise p[n]. Pourrait allouer (n+1)*4, mais c'est plus de caractères.


1

C ++, 166 octets

Golfé:

#include<iostream>
#include <list>
int j(int n,int k){if(n>1){return(j(n-1,k)+k-1)%n+1;}else{return 1;}}
int main(){intn,k;std::cin>>n>>k;std::cout<<j(n,k);return 0;}

Non golfé:

#include<iostream>
#include <list>
int j(int n,int k){
    if (n > 1){
        return (j(n-1,k)+k-1)%n+1;
    } else {
        return 1;
    }
}
int main()
{
    int n, k;
    std::cin>>n>>k;
    std::cout<<j(n,k);
    return 0;
}

2
Vous pouvez économiser des octets sur la Jfonction, en utilisant l'opérateur ternaire.
Yytsi

intndans votre version golfée ne compilera pas
Felipe Nardi Batista

vous pouvez supprimer de l'espace dans#include <list>
Felipe Nardi Batista

1

J, 8 octets

1&|.&.#:

       1&|.&.#: 10
    5

       1&|.&.#: 69
    11

        1&|.&.#: 123456
    115841

        1&|.&.#: 123245678901234567890x NB. x keeps input integral
    98917405212792722853

All credit to Roger Hui, co-inventor of J and all-round uber-genius
www.jsoftware.com for free j software across many platforms

Explanation
    (J works right-to-left)
     #:       convert input to binary
     &.       a&.b <=> perform b, perform a, perform reverse of b
     1&|.     rotate bitwise one bit left

So
    1&|.&.#: 10

    a. #:            convert input (10) TO binary -> 1 0 1 0
    b. 1&|.          rotate result 1 bit left -> 0 1 0 1
    c. due to the &. perform convert FROM binary -> 5 (answer)

1
N'est-il pas censé prendre deux entrées?
Erik the Outgolfer du


1

Q, 34 octets

f:{$[x=1;1;1+mod[;x]f[x-1;y]+y-1]}

Usage:

q)f .'(7 1;7 2;7 3;7 11;77 8;123 12)
7 7 4 1 1 21


0

Haskell, 29

En utilisant la formule de wikipedia.

1#_=1
n#k=mod((n-1)#k+k-1)n+1

0

JavaScript (ECMAScript 5), 48 octets

Utilisation d'ECMAScript 5 car il s'agissait de la dernière version de JavaScript au moment où cette question a été posée.

function f(a,b){return a<2?1:(f(a-1,b)+b-1)%a+1}

Version ES6 (non concurrente), 33 octets

f=(a,b)=>a<2?1:(f(a-1,b)+b-1)%a+1

Explication

Pas grand chose à dire ici. J'implémente juste la fonction que l'article Wikipedia me donne.


0

05AB1E , 11 octets

L[Dg#²<FÀ}¦

Essayez-le en ligne!

L           # Range 1 .. n
 [Dg#       # Until the array has a length of 1:
     ²<F }  #   k - 1 times do:
        À   #     Rotate the array
          ¦ #   remove the first element

0

8ème , 82 octets

Code

: j >r >r a:new ( a:push ) 1 r> loop ( r@ n:+ swap n:mod ) 0 a:reduce n:1+ rdrop ;

SED (Stack Effect Diagram) est:n k -- m

Utilisation et explication

L'algorithme utilise un tableau d'entiers comme celui-ci: si la valeur des personnes est 5 alors le tableau sera [1,2,3,4,5]

: j \ n k -- m
    >r                               \ save k
    >r a:new ( a:push ) 1 r> loop    \ make array[1:n]
    ( r@ n:+ swap n:mod ) 0 a:reduce \ translation of recursive formula with folding using an array with values ranging from 1 to n
    n:1+                             \ increment to move from 0-based to 1-based indexing
    rdrop                            \ clean r-stack
;

ok> 7 1 j . cr
7
ok> 7 2 j . cr
7
ok> 7 3 j . cr
4
ok> 7 11 j . cr
1
ok> 77 8 j . cr
1
ok> 123 12 j . cr
21

0

J , 24 octets

1+1{1([:|/\+)/@,.1|.!.0#

Essayez-le en ligne!

Une approche itérative basée sur la solution de programmation dynamique.

Explication

1+1{1([:|/\+)/@,.1|.!.0#  Input: n (LHS), k (RHS)
                       #  Make n copies of k
                 1|.!.0   Shift left by 1 and fill with zero
    1          ,.         Interleave with 1
             /@           Reduce using
           +                Addition
        |/\                 Cumulative reduce using modulo
  1{                      Select value at index 1
1+                        Add 1

0

J , 19 octets

1+(}:@|.^:({:@])i.)

Essayez-le en ligne!

Comment ça marche

1+(}:@|.^:({:@])i.)   Left: k, Right: n
                i.    Generate [0..n-1]
        ^:            Repeat:
   }:@|.                Rotate left k items, and remove the last item
          ({:@])        n-1 (tail of [0..n-1]) times
1+                    Add one to make the result one-based


0

Japt , 15 octets

_é1-V Å}h[Uõ] Ì

Essayez-le en ligne!

Un octet pourrait être enregistré par l' indexation 0 k , mais ce n'est pas un index, j'ai donc décidé de ne pas le faire.

Explication:

         [Uõ]      :Starting with the array [1...n]
_      }h          :Repeat n-1 times:
 é1-V              : Rotate the array right 1-k times (i.e. rotate left k-1 times)
      Å            : Remove the new first element
              Ì    :Get the last value remaining


0

Powershell, 56 octets

param($n,$k)if($n-lt2){1}else{((.\f($n-1)$k)+$k-1)%$n+1}

Important! Le script s'appelle récursivement. Enregistrez donc le script en tant que f.ps1fichier dans le répertoire courant. Vous pouvez également appeler une variable de bloc de script à la place d'un fichier de script (voir le script de test ci-dessous). Ces appels ont la même durée.

Script de test:

$f = {

param($n,$k)if($n-lt2){1}else{((&$f($n-1)$k)+$k-1)%$n+1}

}

@(
    ,(7, 1, 7)
    ,(7, 2, 7)
    ,(7, 3, 4)
    ,(7, 11, 1)
    ,(77, 8, 1)
    ,(123,12, 21)
) | % {
    $n,$k,$expected = $_
    $result = &$f $n $k
    "$($result-eq$expected): $result"
}

Sortie:

True: 7
True: 7
True: 4
True: 1
True: 1
True: 21
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.