Последовательность пересечения сетки


17

Если вы возьмете лист миллиметровки и нарисуете наклонную линию, которая идет mединицами вправо и nединицами вверх, вы пересекаете n-1горизонтальную и m-1вертикальную линии сетки в некоторой последовательности. Напишите код для вывода этой последовательности.

Например, m=5и n=3дает:

Пересечение линий сетки для m = 5, n = 3

Возможно , связанные с : Генерация евклидово ритмы , разбиений Фибоначчи , FizzBuzz

Входные данные: два натуральных числа, m,nкоторые являются относительно простыми

Вывод: возврат или печать пересечений в виде последовательности двух разных токенов. Например, это может быть строка Hи V, список Trueи False, или 0«s и 1» ы печатаются на отдельных строках. Между токенами может быть разделитель, если он всегда один и тот же, а не, скажем, переменное число пробелов.

Тестовые случаи:

Первый тестовый пример дает пустой вывод или не выводит.

1 1 
1 2 H
2 1 V
1 3 HH
3 2 VHV
3 5 HVHHVH
5 3 VHVVHV
10 3 VVVHVVVHVVV
4 11 HHVHHHVHHHVHH
19 17 VHVHVHVHVHVHVHVHVVHVHVHVHVHVHVHVHV
39 100 HHVHHHVHHVHHHVHHVHHHVHHVHHHVHHHVHHVHHHVHHVHHHVHHVHHHVHHHVHHVHHHVHHVHHHVHHVHHHVHHVHHHVHHHVHHVHHHVHHVHHHVHHVHHHVHHHVHHVHHHVHHVHHHVHHVHHHVHH

В формате (m,n,output_as_list_of_0s_and_1s):

(1, 1, [])
(1, 2, [0])
(2, 1, [1])
(1, 3, [0, 0])
(3, 2, [1, 0, 1])
(3, 5, [0, 1, 0, 0, 1, 0])
(5, 3, [1, 0, 1, 1, 0, 1])
(10, 3, [1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1])
(4, 11, [0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0])
(19, 17, [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1])
(39, 100, [0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0])

2
Сегодня на PPCG: гольф Алгоритм рисования линий Брезенхэма
Спарр

Основываясь на добавленном вами альтернативном формате, можно / нужно повторять ввод как часть вывода? В противном случае, я не понимаю, почему ввод и вывод являются частью одного и того же списка.
Рето Коради

@RetoKoradi Нет, вы не должны включать входные данные. Я поместил его в кортежи, чтобы было проще обрабатывать контрольные примеры.
xnor

Я могу предсказать ответ, но не мешает спросить: допустимо ли использовать символ пробела в качестве одного из выходных токенов? Как следствие, в выходных данных могут быть значительные начальные / конечные пробелы. Не было бы других пробелов, поэтому все пробелы были бы значительными.
Рето Коради

@RetoKoradi Нет, потому что конечные пробелы не видны.
xnor

Ответы:


7

Руби, 92; Страус 0.7.0 , 38

f=->m,n{((1..m-1).map{|x|[x,1]}+(1..n-1).map{|x|[1.0*x*m/n,0]}).sort_by(&:first).map &:last}
:n;:m1-,{)o2W}%n1-,{)m n/*0pW}%+_H$_T%

Вывод для них обоих использует 1 и 0 (например 101101).


Вот объяснение страуса:

:n;:m    store n and m as variables, keep m on the stack
1-,      from ex. 5, generate [0 1 2 3]
{...}%   map...
  )        increment, now 5 -> [1 2 3 4]
  o        push 1 (the digit 1 is special-cased as `o')
  2W       wrap the top two stack elements into an array
           we now have [[1 1] [2 1] [3 1] [4 1]]
n1-,     doing the same thing with n
{...}%   map...
  )        increment, now 3 -> [1 2]
  m n/*    multiply by slope to get the x-value for a certain y-value
  0        push 0
  pW       wrap the top two stack elements (2 is special-cased as `p')
+        concatenate the two arrays
_H$      sort-by first element (head)
_T%      map to last element (tail)

И объяснение того, как все это работает, используя код Ruby в качестве руководства:

f = ->m,n {
    # general outline:
    # 1. collect a list of the x-coordinates of all the crossings
    # 2. sort this list by x-coordinate
    # 3. transform each coordinate into a 1 or 0 (V or H)
    # observation: there are (m-1) vertical crossings, and (n-1) horizontal
    #   crossings. the vertical ones lie on integer x-values, and the
    #   horizontal on integer y-values
    # collect array (1)
    (
        # horizontal
        (1..m-1).map{|x| [x, 1] } +
        # vertical
        (1..n-1).map{|x| [1.0 * x * m/n, 0] }  # multiply by slope to turn
                                               # y-value into x-value
    )
    .sort_by(&:first)  # sort by x-coordinate (2)
    .map &:last        # transform into 1's and 0's (3)
}

5

Python, 53

Это использует вывод списка True / False. Здесь нет ничего особенного.

lambda m,n:[x%m<1for x in range(1,m*n)if x%m*(x%n)<1]

4

Pyth - 32 24 байта

Jmm,chk@Qddt@Qd2eMS+hJeJ

Принимает ввод через стандартный ввод в формате [m,n]. Выводит результат в стандартный вывод в виде списка 0 и 1, где 0 = V и 1 = H.

Протестируйте это онлайн


Объяснение:

J                           # J = 
 m             2            # map(lambda d: ..., range(2))
  m        t@Qd             # map(lambda k: ..., range(input[d] - 1))
   ,chk@Qdd                 # [(k + 1) / input[d], d]
                eMS+hJeJ    # print map(lambda x: x[-1], sorted(J[0] + J[-1])))

Вы можете сохранить байт, используя оператор синтаксической карты для конца отображения. eMтак же, как med.
Maltysen

Кроме того, вы можете просто вынуть, @"VH"так как вам разрешено печатать, 0а 1не Vи H.
Малтысен,

Вы можете сохранить еще один байт, используя встроенное назначение с J. Вот то, что я имею до сих пор в 25 байтах: pyth.herokuapp.com/…
Maltysen

@ Maltysen, спасибо, я думаю, что вы можете удалить, так jkкак вывод может быть список.
Tyilo

Вы можете получить 23 взгляда на мой комментарий о встроенном назначении.
Малтысен

4

Машинный код IA-32, 26 байтов

Hexdump кода:

60 8b 7c 24 24 8d 34 11 33 c0 2b d1 74 08 73 03
03 d6 40 aa eb f2 61 c2 04 00

Я начал со следующего кода C:

void doit(int m, int n, uint8_t* out)
{
    int t = m;
    while (true)
    {
        if (t >= n)
        {
            t -= n;
            *out++ = 1;
        }
        else
        {
            t += m;
            *out++ = 0;
        }
        if (t == n)
            break;
    }
}

Он записывает вывод в предоставленный буфер. Он не возвращает длину вывода, но это на самом деле не нужно: длина вывода всегдаm + n - 2 :

int main()
{
    char out[100];
    int m = 10;
    int n = 3;
    doit(m, n, out);
    for (int i = 0; i < m + n - 2; ++i)
    {
        printf("%d ", out[i]);
    }
}

Чтобы преобразовать код C в машинный код, я сначала сделал некоторые настройки, чтобы одна из if/elseветок была пустой, и сравнил 0вместоn :

void doit(int m, int n, char* out)
{
    int t = n;
    while (true)
    {
        int r = 0;
        t -= m;
        if (t == 0)
            break;
        if (t >= 0)
        {
        }
        else
        {
            t += m + n;
            ++r;
        }
        *out++ = r;
    }
}

Отсюда написание встроенного ассемблера просто:

__declspec(naked) void __fastcall doit(int x, int y, char* out)
{
    _asm
    {
        pushad;                 // save all registers
        mov edi, [esp + 0x24];  // edi is the output address
        lea esi, [ecx + edx];   // esi is the sum m+n
    myloop:                     // edx is the working value (t)
        xor eax, eax;           // eax is the value to write (r)
        sub edx, ecx;
        jz myout;
        jae mywrite;
        add edx, esi;
        inc eax;
    mywrite:
        stosb;                  // write one value to the output
        jmp myloop;
    myout:
        popad;                  // restore all registers
        ret 4;                  // return (discarding 1 parameter on stack)
    }
}

Мне любопытно - почему этот алгоритм работает?
xnor

@xnor Неофициально, он отслеживает последовательность fizzbuzz . Вот tэто «расстояние до buzz». Если расстояние хотя бы n, иди fizz, а иди buzz; обновить расстояние; повторять, пока не будет 0.
Анатолиг

3

Python - 125 байт

Использует очень простой алгоритм, просто увеличивает координаты и определяет, когда он пересекает линии и печатает. Ищу перевести на Pyth.

a,b=input()
i=1e-4
x=y=l=o=p=0
k=""
while len(k)<a+b-2:x+=i*a;y+=i*b;k+="V"*int(x//1-o//1)+"H"*int(y//1-p//1);o,p=x,y
print k

Это время цикла проверяет количество l ines, а затем проверяет, перешло ли какое-либо из значений за границу int путем вычитания.

Принимает ввод как 39, 100из стандартного ввода и печатает как HHVHHHVHHVHHHVHHVHHHVHHVHHHVHHHVHHVHHHVHHVHHHVHHVHHHVHHHVHHVHHHVHHVHHHVHHVHHHVHHVHHHVHHHVHHVHHHVHHVHHHVHHVHHHVHHHVHHVHHHVHHVHHHVHHVHHHVHHвывод в одну строку.


3

CJam, 15 байтов

Ll~_:*,\ff{%!^}

Попробуй это здесь.

Он печатает 01для V и10 для H.

объяснение

L          e# An empty list.
l~         e# Evaluate the input.
_:*,       e# [0, m*n).
\          e# The input (m and n).
ff{%!      e# Test if each number in [0, m*n) is divisible by m and n.
^}         e# If divisible by m, add an 10, or if divisible by n, add an 01 into
           e# the previous list. If divisible by neither, the two 0s cancel out.
           e# It's just for output. So we don't care about what the previous list
           e# is -- as long as it contains neither 0 or 1.

Диагональная линия пересекает горизонтальную линию для каждого 1 / n всей диагональной линии и пересекает вертикальную линию для каждого 1 / м.


Вы добавите объяснение этому? Это очень интригующе, но по крайней мере с первого взгляда я не понимаю, почему это работает. Играя с ним, я замечаю, что он работает только для значений, которые являются относительными простыми числами (что дано в описании проблемы), в то время как мой работает для всех значений. Таким образом, основная математика, очевидно, очень отличается.
Рето Коради

После того, как я позволил этому немного погрузиться, я думаю, что понимаю, по крайней мере, часть алгоритма. Придется изучить логику генерации выходов позже.
Рето Коради

@RetoKoradi Отредактировано.
jimmy23013

2

TI-BASIC, 32

Prompt M,N
For(X,1,MN-1
gcd(X,MN
If log(Ans
Disp N=Ans
End

Непосредственная. Использует последовательность 0и 1, разделенных переносами строк. Преимуществами TI-BASIC являются двухбайтовое gcd(и подразумеваемое умножение, но его недостатками являются цикл For, включающий конечное значение и 5 байтов, затраченных на ввод.



1

Haskell, 78 байт

import Data.List
m#n=map snd$sort$[(x,0)|x<-[1..m-1]]++[(y*m/n,1)|y<-[1..n-1]]

Пример использования:

*Main> 19 # 17
[0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0]
*Main> 10 # 3
[0,0,0,1,0,0,0,1,0,0,0]

Как это работает: составьте список значений x всех вертикальных пересечений (x,0)для xв [1,2, ..., m-1] ( 0указывает на вертикаль) и добавьте список значений x всех горизонтальных пересечений (y*m/n,1)для yв [1,2, ..., n-1] ( 1указывает на горизонтальность). Сортируйте и извлекайте вторые элементы из пар.

Проклятие дня: опять же я должен потратить 17 байтов, importпотому что он sortесть, Data.Listа не в стандартной библиотеке.


1

KDB (Q), 44 байта

{"HV"0=mod[asc"f"$1_til[x],1_(x*til y)%y;1]}

объяснение

Найти все значения оси X точек пересечения и отсортировать их. Если mod 1 равен нулю, его «V», ненулевое значение «H».

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

q){"HV"0=mod[asc"f"$1_til[x],1_(x*til y)%y;1]}[5;3]
"VHVVHV"

1

CJam, 26 24 байта

l~:N;:M{TN+Mmd:T;0a*1}*>

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

Очень простая, в значительной степени прямая реализация алгоритма типа Брезенхэма.

Объяснение:

l~    Get input and convert to 2 integers.
:N;   Store away N in variable, and pop from stack.
:M    Store away M in variable.
{     Loop M times.
  T     T is the pending remainder.
  N+    Add N to pending remainder.
  M     Push M.
  md    Calculate div and mod.
  :T;   Store away mod in T, and pop it from stack
  0a    Wrap 0 in array so that it is replicated by *, not multiplied.
  *     Emit div 0s...
  1     ... and a 1.
}*      End of loop over M.
>       Pop the last 1 and 0.

Последнее 01нужно вытолкнуть, потому что цикл прошел весь путь до конечной точки, что не является частью желаемого результата. Обратите внимание, что мы не можем просто уменьшить количество циклов на 1. В противном случае N > Mвсе 0s из последней итерации будут отсутствовать, а нам нужно только избавиться от самой последней 0.


1
Вы можете использовать >для ;W<.
jimmy23013

@ jimmy23013 Хорошая идея. Поскольку я знаю, что у меня есть 1верхняя часть стека, я мог бы также использовать его продуктивно.
Рето Коради
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.