Считать квадраты


18

Вызов

Оригами (складная бумага) - это творческий вид искусства. Насколько я знаю, мастер оригами предпочитает квадратную бумагу. Начнем с начала - конвертируем прямоугольную бумагу в квадратную.

Таким образом, бумага делится на квадраты. Мы удаляем самый большой квадрат, который разделяет один более короткий край с текущей формой, шаг за шагом (см. Рисунок ниже). И если оставшаяся часть после одного шага меньше или равна 0.001 * (area of the original paper), бумага не может быть разделена дальше. Возможно, что в конце концов ничего не останется.

Ваша задача - подсчитать, сколько квадратов сделано в процессе. Квадрат на последнем шаге, который не позволяет разделить бумагу, засчитывается в результат.

Пример (бумага 1.350ширины / высоты), вывод 10:

пример среза

Вход и выход

Ввод: отношение ширины к высоте для прямоугольной бумаги, один десятичный знак (или целое число без точки) от 1.002до 1.999с минимальным шагом 0.001. Вы также можете использовать любой другой разумный формат, описывающий соотношение. Просто упомяните это в своем ответе.

Вывод: квадратное число, одно целое число.

Пример ввода / вывода

Формат отображения используется для поддержания чистоты страницы, в то время как ваш код не должен поддерживать ввод списка или быть функцией отображения.

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

Список всех ответов

Спасибо @LuisMendo, вот график ответов.

график

замечания

  • Это код-гольф, поэтому выигрывает самый короткий код
  • Обратите внимание на стандартные лазейки
  • Вы можете сами решать, что делать с вводом и выводом, но они должны следовать стандартным ограничениям.

Кстати...

  • Прокомментируйте, если у вас есть что-то неясное по поводу проблемы
  • Лично я бы предложил, чтобы ваш ответ содержал объяснение, если вы используете язык игры в гольф
  • Спасибо @GregMartin, прочитайте его ответ для хорошего математического объяснения проблемы.

Пример кода

Вот незакрашенная версия кода 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;
}

Все вычисления, относящиеся к примеру кода, требуют точности 6 десятичных цифр, которая покрыта float.


Можно ли использовать в качестве входных данных два числа, формирующие соотношение?
Луис Мендо

@ LuisMendo да, по вашему желанию.
Кейу Ган

2
Аккуратный вызов!
flawr

5
Список ответов дает хороший график
Луис Мендо

1
@KeyuGan Конечно, давай! Дайте мне знать, если вам нужна версия с другим форматом
Луис Мендо

Ответы:


2

MATL , 19 байт

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

Входные данные представляют собой массив с двумя числами, определяющими исходное соотношение, например [1, 1.009]. (Не обязательно, чтобы числа были отсортированы или чтобы одно из них было 1.)

Попробуйте онлайн!

объяснение

`        % 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)

6

Haskell , 71 70 65 63 62 61 58 56 байт

Спасибо @xnor за некоторые гениальные улучшения!

(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

Попробуйте онлайн!


Думаю, что m==nв конце может быть, 1>0потому что это единственная оставшаяся возможность. Или, может быть, дела могут быть переставлены, чтобы связать здесь.
xnor

На самом деле, нужен ли случай равенства? Если n>mрасширен до n>=mи написана первая проверка e>m*n*1000, это должно дать 1равенство.
xnor

@xnor Хорошая идея, спасибо!
flawr

1
Перемещение охранников в течение 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

Ничего себе, используя d<-n-mкак otherwiseдействительно опрятно !!!
flawr

4

JavaScript (ES6), 59 58 байт

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

Тестовое задание


4

Mathematica, не конкурирующий (21 байт)

Этот ответ не конкурирует, потому что он не отвечает на фактически заданный вопрос! Но он действительно отвечает на вариант вопроса и дает повод для выделения интересной математики.

Tr@*ContinuedFraction

Символьная функция, принимающая положительное рациональное число в качестве входных данных (числитель и знаменатель которых представляют размеры оригинальной статьи) и возвращающая положительное целое число. Например, Tr@*ContinuedFraction[1350/1000]возвращает 10. ( ContinuedFractionдействует по-разному для чисел с плавающей точкой из-за проблем с точностью, поэтому в качестве входных данных в этом контексте требуется рациональное число.)

Интересная интерпретация геометрической процедуры, описанной в задаче (многократное отрезание квадратов от прямоугольника), заключается в том, что это реализация евклидова алгоритма для нахождения наибольших общих делителей! Рассмотрим пример в самом вопросе с соотношением1.35, который можно смоделировать, имея лист бумаги с размерами (1350, 1000). Каждый раз, когда квадрат обрезается, меньшее число вычитается из большего числа; поэтому получающиеся прямоугольники в этом примере имеют размеры (350, 1000), затем (350 650), затем (350 300), затем (50 300), затем (50 250) и (50 200) и (50 150) и (50 100) и (50, 50), а также (50,0), как только мы заберем у себя последний квадрат. Именно так работает евклидов алгоритм (по модулю разницы между делением и повторным вычитанием), и мы действительно видим, что 50 - это действительно GCD из 1350 и 1000.

Как правило, в евклидовом алгоритме каждый отслеживает эти промежуточные измерения и отбрасывает количество вычитаний; Тем не менее, можно поочередно записать, сколько раз мы вычитали одно число из другого, прежде чем разница станет слишком маленькой, и мы должны изменить то, что мы вычитаем. Этот метод записи является именно непрерывной дробью рационального числа. (Непрерывные дроби иррациональных чисел, которые никогда не заканчиваются, также очень крутые, но здесь они не актуальны.) Например, в примере 1350/1000 мы вычитали 1000 1раз, затем 350 2раз, затем 300 1раз, затем 50 6раз; следовательно, продолжающаяся дробь 1350/1000 есть {1,2,1,6}. Математически мы переписали 1350/1000 как 1+ 1 / ( 2+ 1 / ( 1+ 1 /)6)), который вы можете проверить.

Так что для этой проблемы, если вы не останавливаетесь, когда квадраты становятся меньше, чем определенное пороговое значение, а просто подсчитываете все конечное число квадратов до того, как оно остановится, тогда общее количество квадратов равно общему количеству вычитаний, то есть сумма всех целых чисел в непрерывной дроби - и это именно то, что Tr@*ContinuedFractionвычисляет композиция функций ! (Для данного примера 1.35, он получает ответ, который желает OP, потому что последний квадрат достаточно велик, чтобы были подсчитаны все квадраты. Но Tr@*ContinuedFraction[1001/1000], например, дает 1001, так как он учитывает один огромный квадрат и все 1000 из маленьких квадратов 1x1000 .)


1
Хотя это действительно интересно, неконкурентный ярлык зарезервирован для языков, которые новее, чем проблема . Независимо от этого, все ответы должны решить проблему. Следовательно, этот ответ действительно должен быть удален. Сможете ли вы восстановить из списка продолженных дробей, где его обрезать, чтобы это могло быть превращено в не менее интересное, но правильное решение?
Мартин Эндер

1
Когда я писал этот ответ, у меня был умственный зуд, но я согласен, что это достойный удаления ответ согласно стандартам сообщества. (Спасибо за Вашу мягкую, но точную обратную связь!) Если TPTB захочет отложить удаление на 24 часа, я мог бы уточнить подход, чтобы получить правильный ответ ... если нет, удалить и никаких неприятных ощущений.
Грег Мартин

3

Mathematica, 64 53 байта

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

Императивное (в стиле C) решение точно такой же длины:

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

2

C (GCC / Clang), 61 59 байт

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

Ввод двух целых чисел (ширина и высота) без точки, например f(1999,1000).

Я надеюсь, что кто-то может сохранить один байт, вставив C в 58-байтовый клуб. ;)


Предложите убрать круглые скобки вокруг m-=n
потолка

1

C 59 байт

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

Попробуй онлайн

Входные данные представляют собой целое число, которое представляет собой отношение ширины к высоте в тысячных долях (например, 1002 для 1,002: 1).

Неуправляемая версия

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;
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.