Проблема Йосифа (відлік)


29

Змагання

Напишіть функцію, яка приймає два цілих натуральних числа n та k як аргументи і повертає число останньої особи, що залишилася поза n після відрахування кожної k- ї особи.

Це виклик з гольф-кодом, тому виграє найкоротший код.

Проблема

n людей (пронумеровані від 1 до n ) стоять у колі, і кожен k -й підраховується, поки не залишиться жодної людини (див. відповідну статтю вікіпедії ). Визначте кількість цієї останньої людини.

Наприклад, k = 3 дві людини будуть пропущені, а третя буде відлічена. Тобто для n = 7 числа будуть підраховані в порядку 3 6 2 7 5 1 (детально 1 2 3 4 5 6 7 1 2 4 5 7 1 4 5 1 4 1 4 ), і таким чином відповідь дорівнює 4 .

Приклади

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

Відповіді:


5

GolfScript, 17 байт

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

Бере n kна стек, а результат залишає на стеку.

Розсічення

Для цього використовується рецидив g(n,k) = (g(n-1,k) + k) % nз g(1, k) = 0(як описано у статті у Вікіпедії), а рекурсія замінена складкою.

{          # 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;

Чи можете ви додати пояснення, будь ласка?
Шерлок9

@ Sherlock9, мені вдалося зрозуміти, що я роблю, незважаючи на те, що минуло майже 3,5 роки. Хто каже, що GolfScript є лише для читання? ;)
Пітер Тейлор

Ахм. s / читати / писати /
Пітер Тейлор

Вибачте. Я почав вивчати Golfscript два-три дні тому, і кожен раз, коли я читав ваш код, я думав, що щось пропустив. ... Гаразд, мені все ще щось не вистачає, як складання {k block}над [0..n-1]вами змушує вас g(0,k) 0 kпочати? Вибачте, якщо я публікую ці запитання в іншому місці.
Шерлок9

@ Sherlock9, fold працює попарно, тому перше, що він робить - це оцінити 0 1 block. Це дуже зручно g(1, k) (2-1) block. Так що це починається, g(1,k) 1а не g(0,k) 0. Потім після виконання блоку він виштовхує наступний елемент з масиву ( 2) і знову виконує блок тощо.
Пітер Тейлор

14

Мінський реєстровий автомат (25 держав, що не мають зупинки)

Технічно не функція, але це в обчислювальній парадигмі, яка не має функцій сама по собі ...

Це незначна зміна щодо основного тестового випадку мого першого виклику інтерпретації MRM : Проблема Йосифа як реєстраційної машини Мінського

Введення в регістри nта k; вихід у регістр r; передбачається, що r=i=t=0при вступі. Перші дві інструкції щодо зупинки - це випадки помилок.


Я думаю, вам доведеться трохи відрегулювати машину. Якщо я прочитав його правильно, вихід нульово індексується, чи не так?
Говард

Я думав інакше: якщо k=1тоді r=0. Хм, я мушу ще раз подумати над цим ...
Говард

Коли я читаю вашу діаграму, iце просто підрахунок часу 2до того nчасу, rпоки реєстр, який акумулює результат.
Говард

@Howard, я подивився на коментарі, які я зробив, коли я вперше написав це, і ти маєш рацію. Уопс. Тепер виправлено (я вважаю - пізніше випробую).
Пітер Тейлор

7

Пітона, 36

Я також використав формулу з Вікіпедії:

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

Приклади:

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


5

C, 40 символів

Це майже просто формула, яку дає вищезгадана стаття Вікіпедії:

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

Для різноманітності, ось реалізація, яка фактично виконує моделювання (99 символів):

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
Зберегти символ: j(n,k){return--n?(j(n,k)+k-1)%-~n+1:1;}.
ugoren

5

постійного струму, 27 байт

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

Використовується повторення із статті Вікіпедії. Пояснення:

# 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 символів

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

Запускається моделювання.

Крім того, використовуючи формулу (31 символ):

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

Я сподіваюся, що Говард не заперечує, що я трохи відкоригував формат введення, щоб відповідати діадезному дієслову в Дж.

Використання:

   7 j 3
4
   123 j 12
21

4

GolfScript, 32 24 байти

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

Використання: Очікує, що два параметри n і k будуть знаходитися в стеку і залишає вихідне значення.

(дякую Пітеру Тейлору за те, що він запропонував ітеративний підхід та багато інших порад)

Старий (рекурсивний) підхід 32 символи:

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

Це мій перший GolfScript, тому, будь ласка, повідомте мене про вашу критику.


1
1-має спеціальний код (. Так само 1+є ). Вам не потрібно використовувати алфавітні символи для зберігання, тому ви можете використовувати, наприклад, ^замість цього Jі не потребувати місця після нього. У вас набагато більше $s, ніж зазвичай, у добре розграбованій програмі: подумайте, чи можете ви зменшити їх за допомогою комбінації \@..
Пітер Тейлор

@PeterTaylor Дякую велике за ці чудові поради! Досить важко зрозуміти всіх операторів Golfscript, і я не помітив цих двох дуже простих. Тільки застосовуючи перші дві пропозиції, мені вдається скоротити код на 5 символів. Я також спробую видалити $посилання.
Крістіан Лупаску

1
Крім того, рекурсія насправді не є GolfScript. Спробуйте перевернути його і зробити петлю. Я можу зменшити його до 19 символів (хоча і не перевіреного коду) таким чином. Підказка: розгорніть функцію gзі статті Вікіпедії та використовуйте ,та /.
Пітер Тейлор

1
{,{\2$+\)%}*)\;}:f;Переконайтесь, що ви зрозуміли, чому це працює;)
Пітер Тейлор

1
Один заключний трюк: замість того, щоб використовувати два символи для доступу kдо циклу, а потім ще 2, щоб відкинути його в кінці, ми можемо втягнути його всередину, використовуючи, +щоб перейти до 17 символів: {{@+\)%}+\,*)}:f;я сумніваюся, що це можна покращити.
Пітер Тейлор





1

C, 88 символів

Чи робить моделювання, не обчислює формулу.
Набагато довше, ніж формула, але коротше, ніж інше моделювання С.

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;
}

Примітки:
1. Виділяє пам'ять і ніколи не випускає.
2. Виділяє n*8замість n*4, тому що я використовую p[n]. Можна виділити (n+1)*4, але це більше символів.


1

C ++, 166 байт

Гольф:

#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;}

Безголовки:

#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
Ви можете зберегти байти на Jфункції за допомогою потрійного оператора.
Yytsi

intnу вашій версії для гольфу не збиратимуться
Феліпе Нарді Батіста

ви можете прибрати простір у#include <list>
Феліпе Нарді Батіста

1

J, 8 байт

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
Чи не слід брати два входи?
Ерік Аутгольфер


1

Q, 34 байти

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

Використання:

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



0

JavaScript (ECMAScript 5), 48 байт

Використовуючи ECMAScript 5, оскільки це було останньою версією JavaScript на той момент, коли це питання було задано.

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

Версія ES6 (неконкурентна), 33 байти

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

Пояснення

Тут мало що сказати. Я просто реалізую функцію, яку надає мені стаття у Вікіпедії.



0

8-е , 82 байти

Код

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

SED (діаграма ефектів стеку):n k -- m

Використання та пояснення

Алгоритм використовує масив цілих чисел на кшталт цього: якщо значення людей дорівнює 5, то масив буде [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 байти

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

Спробуйте в Інтернеті!

Ітеративний підхід, заснований на рішенні з динамічним програмуванням.

Пояснення

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

Japt , 15 байт

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

Спробуйте в Інтернеті!

Байт можна зберегти за допомогою 0-індексації k , але це насправді не індекс, тому я вирішив проти цього.

Пояснення:

         [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 байт

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

Важливо! Сценарій називає себе рекурсивно. Тому збережіть сценарій як f.ps1файл у поточному каталозі. Також ви можете викликати змінну блоку скриптів замість файлу сценарію (див. Тестовий сценарій нижче). Ці дзвінки мають однакову тривалість.

Тестовий сценарій:

$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"
}

Вихід:

True: 7
True: 7
True: 4
True: 1
True: 1
True: 21
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.