Перебор коммутатора


32

На днях наша команда отправилась в комнату побега. Одна из головоломок была связана с платой из шести механических переключателей, где вам нужно было найти правильную комбинацию включения и выключения, чтобы разблокировать коробку, примерно так:

-v-v-v-
-v-v-v-

Будучи разработчиками, мы решили, что было бы эффективнее попробовать каждую из 2 ^ 6 = 64 комбинаций, чем решить головоломку. Итак, мы назначили какого-то бедного парня для двоичного подсчета:

-v-v-v-
-v-v-v-

-v-v-v-
-v-v-^-

-v-v-v-
-v-^-v-

-v-v-v-
-v-^-^-

и так далее.

Задача
Напишите программу, которая, учитывая все переключатели в выключенном положении в виде строки, как указано выше, генерирует все комбинации включения и выключения в любом порядке.

Вы можете написать либо полную программу, либо функцию. Таким образом, ваша программа может принимать входные данные через stdin, файл или в виде строкового аргумента, а также возвращать или распечатывать выходные данные. Если возвращено, вывод может быть в списке / массиве и т. Д. а не одна строка. Если выходные данные представляют собой одну строку, доски должны быть разделены символами новой строки (конечные символы новой строки разрешены).

Входные строки будут соответствовать регулярному выражению r'((-v)+-)(\n(-v)+-)*'и представляют одну плату со всеми выключенными выключателями. Это означает отсутствие нуля, и переключатели выровнены по левому краю. Каждая строка может не иметь одинакового количества переключателей.

Каждая плата вывода должна быть точно такого же формата, что и входные данные, за исключением того, что буквы v могут быть заменены символами ^ при необходимости. Таблицы вывода могут быть разделены любым количеством новых строк.

Поскольку время выполнения, естественно, равно O (2 ^ n) по количеству переключателей, ваш код не будет тестироваться на более чем 10 переключателях в любом порядке.

Это код-гольф, поэтому выигрывает самый короткий код в количестве байтов.

Образцы входов и выходов

Входные данные:

-v-

Возможный вывод:

-v-
-^-

Входные данные:

-v-
-v-

Возможный вывод:

-^-
-^-
-^-
-v-

-v-
-^-


-v-
-v-

Поскольку проверять ваш ответ на большее количество переключателей крайне утомительно, вот скрипт Python как инструмент проверки работоспособности. (Я включил закомментированный в настоящее время фрагмент кода для генерации ожидаемого вывода из заданного входного файла на случай, если вам нужно больше тестовых случаев.) К сожалению, он немного менее гибок в плане ввода и вывода, чем спецификация; поместите входную строку в файл с именем 'input', а вывод с разделителями новой строки (извините, форматирование списка отсутствует) в файл с именем 'output' в том же каталоге и запустите python3 sanitycheck.py.


8
хороший первый вызов!
Джузеппе

12
Будем надеяться, что «бедняга» знал о коде Грея , чтобы переключать только один бит между каждой комбинацией.
Эрик Думинил

1
Время - наш самый ценный актив, не трать его зря.
Педро Лобито

6
Учитывая тему, я разочарован тем, что вам не потребовался заказ, который требует наименьшего количества переключателей (например, 00-> 01-> 11-> 10 имеет 3 переключателя, в то время как 00-> 01-> 10-> 11 имеет 4 переключателя ) - Товарищ по перебору
икегами

2
@EricDuminil: если бы механические переключатели не были кнопками (и, может быть, даже если), то, скорее всего, разница во времени, необходимом для переключения одного, двух и трех последовательных переключателей (что вы, вероятно, могли бы сделать почти одновременно), была бы недостаточно большой чтобы компенсировать дополнительную умственную работу, чтобы следовать кодексу Грея.
Томас

Ответы:


23

Haskell , 25 24 23 17 байт

mapM$min"^v".pure

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

-1 байт благодаря @ H.PWiz

-1 байт благодаря @nimi

Возвращает список строк. У TIO есть 2 дополнительных байта для объявления функции - я видел, как другие люди оставляли это, когда они пишут функцию pointfree, поэтому я делаю то же самое, если не указано иное.

Предыдущий ответ (25 байт)

g 'v'="v^"
g x=[x]
mapM g

Все объяснения относятся к предыдущему ответу, который работает примерно так же, за исключением того, что я включил определение g. Способ gработает теперь с помощью лексического сравнения заменить ^vнаv и держать все остальное то же самое.

Интересно, что это работает для произвольных коммутаторов:

>>> mapM g "-----^-----"
  ["-----v-----", "-----^-----"]

Объяснение (короткое)

g 'v'="v^" -- for 'v', choose between 'v' or '^'
g x=[x]    -- for any other character, choose just that character
mapM g     -- find all ways to choose characters using g on the given input

Объяснение (Длинное)

mapMэто довольно страшная функция для тех, кто не знаком с Haskell. Но это не сложно понять в этом контексте. Заставив его действовать на Strings (который в Haskell - списки символов), я специализировал его для определения списков. Таким образом, в этом контексте его тип подписи

mapM :: (a -> [b]) -> [a] -> [[b]]
--      ^^^^^^^^^^                  arg 1: a function from any a to a list of b
--                    ^^^           arg 2: a list of a
--                           ^^^^^ return: a list of list of b

Это на самом деле еще более специализировано в моем использовании - aи bоба Char- так что мы можем увидеть сигнатуру типа как

mapM :: (Char -> String) -> String -> [String]

Давайте быстро посмотрим, что gделает, прежде чем объяснять, как mapMработает.

g :: Char -> String
g 'v' = "v^"
g  x  = [x]

gиспользует сопоставление с шаблоном для преобразования Char 'v'в строку "v^"; все остальное преобразуется в одноэлементную строку (помните, что строки - это просто списки Chars, поэтому мы можем поместить их xв одноэлементный список). Тестируя на REPL, мы находим, что это так

>>> g 'a'
  "a"
>>> g 'b'
  "b"
>>> g 'v'
  "v^"

Обратите внимание, что g имеет правильный тип, чтобы быть аргументомmapM (неудивительно!).

Мы рассмотрим, как это mapMработает, предоставив его gи аргумент

"-v-\n-v-"

в качестве ввода.

mapMпервые карты gнад String, и поскольку gпреобразует Chars в Strings, это дает нам списокStrings

["-", "v^", "-", "\n", "-", "v^", "-"]

Хотя это правильный тип вывода, mapMделает немного больше. Вы можете думать об этом как о формировании всегоString s, которые вы можете создать из этого списка, если вам нужно было выбрать один символ из каждогоString в нем (по порядку).

Поэтому для первого элемента у вас нет другого выбора, кроме как выбрать Char '-'. Для второго элемента вы можете выбрать между'v' и'^' так далее, и так далее.

Это примерно эквивалентно этому коду Python:

result = []
for x1 in "-":
  for x2 in "v^":
    for x3 in "-":
      ...
        result.append(''.join([x1, x2, x3, x4, x5, x6, x7]))

За исключением того, что, поскольку Haskell разделяет Chars и Strings, когда он помещаетChar s в список, он им не нужен join.

Итоговый результат

["-v-\n-v-", "-v-\n-^", "-^-\n-v-", "-^-\n-^-"]

по желанию.


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

2
@ Rin'sFouriertransform Я был доволен тем, как хорошо mapMсработал этот вызов, сначала я сформулировал его так, sequence . map gно это можно выразить компактно, а mapM id . map gзатем я увидел, что могу простоmapM g
Коул

1
Я думаю, что вы можете поменяться =='v'на>'-'
H.PWiz

9

Perl 6 , 32 байта

{[X~] .comb».&{$_,('^'if /v/)}}

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

  • .comb разбивает строку на символы.
  • ».&{...} сопоставляет символы в соответствии с функцией между фигурными скобками.
  • $_, ('^' if /v/)создает список альтернатив для каждого символа. Только vесть альтернатива ^.
  • [X~]сокращает этот список с помощью оператора перекрестных произведений конкатенации строк X~.

9

Желе , 7 байт

«Ƭ€”^Œp

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

Выводит список строк Jelly.

Объяснение:

«Ƭ€”^Œp  Arguments: 1
«Ƭ€”^    Dyad-nilad pair
  €       Map over left argument
 Ƭ         Apply repeatedly until a result that has previously been seen is seen
           again, return original and intermediate results
«           Dyad: Minimum of arguments
   ”^     Nilad: Literal: '^'
         Note: 'v' is the only character that is greater than '^' and can
         appear in the input, so, while for every character c other than 'v'
         this operation returns [c], for 'v' it returns ['v', '^']. In this way,
         duplicates are never going to appear in the output.
     Œp  Monad: Cartesian product of elements

Я на самом деле копался в кодовых страницах Jelly, пытаясь выяснить, как первый ответ постоянно превосходил каждый другой ответ, почти всегда с довольно хорошим запасом ... Не могли бы вы объяснить, как это работает?
преобразование Фурье Рин

@ Rin'sFouriertransform Я добавил объяснение.
Эрик Outgolfer

6

Perl 5 , 29 байт

sub{glob"\Q@_"=~s/v/{v,^}/gr}

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

Моя первая подача!


Как правило, игроки в Perl 5 представляют программы вместо функций, чтобы избежать необходимости включать их sub{}как минимум. Но они должны добавить say, say␠, say forили say for␠в обмен.

Пройдя суб подход, я мог бы сократить

say for glob"\Q$_"=~s/v/{v,^}/gr        # Perl 5, -0n, 32 bytes

в

sub{glob"\Q@_"=~s/v/{v,^}/gr}           # Perl 5, 29 bytes

Объяснение довольно простое. В Perl 5 есть встроенный globоператор, который принимает шаблон глобуса в виде оболочки, который можно использовать для создания списков имен файлов (например foo*.txt) или списка строк (например {a,b,c}). Уловка в том, что нужно экранировать символ новой строки, что я и сделал quotemeta(as \Q).



4

APL (Dyalog Classic) , 21 17 15 байт

⊃⊢∘.,.∪'v'r'^'

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

похоже на мое решение к

возвращает n-мерный массив строк (n = количество переключателей)

в более легкой для объяснения форме: ⊃(∘.,⌿ ⊢ ∪¨ 'v'⎕r'^')

'v'⎕r'^'заменить vs на^ s

⊢ ∪¨... союзы с каждым из оригинальных персонажей. это вектор строк длиной 1 или 2

∘.,⌿ сокращение декартовых произведений

раскрывать

чтобы добраться до полностью гольфовой версии, мы следуем схеме f⌿ A g¨ B-> A f.g B:

∘.,⌿ ⊢ ∪¨ 'v'⎕r'^' -> ⊢ ∘.,.∪ 'v'⎕r'^'

в качестве побочного эффекта скобки больше не нужны


Все, что имеет внутренний продукт внешнего продукта, заслуживает +1.
Адам

3

J , 42 байта

]`('v'I.@e.~[)`[}"1'v^'{~2#:@i.@^1#.e.&'v'

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

объяснение

]`('v' I.@e.~ [)`[}"1 ('v^' {~ 2 #:@i.@^ 1 #. e.&'v')

Давай возьмем

-v-
-v-

как наш пример ввода.

  • ('v^' {~ 2 #:@i.@^ 1 #. e.&'v')создает все возможные комбинации только переключателей, игнорируя формат ввода. для нашего примера это производит:

    vv
    v^
    ^v
    ^^
    
    • 1 #. e.&'v'считает числа vs на входе.
    • 2 #:@i.@^поднимает 2 до этой степени, производит целые числа от 0 до этого числа i.и преобразует их в двоичные#:
    • 'v^' {~изменения в двоичные цифры vи^
  • ]`('v' I.@e.~ [)`[}"1вносит изменения в исходный входной, производя одну копию для каждой строки результата описано в предыдущем шаге (то есть, все возможные v/ ^комбо). В каждом экземпляре vисходные данные заменяются одной возможной последовательностью v/ ^.

3

Ява, 202 197 189 191 байт

Да, это сравнительно многословный язык, но это то, что я считаю классическим гольфом:

import java.util.function.Function;

public class SwitchBored
{
    public static void main(String[] args)
    {
        Function<String, String> f = s->{byte i,j,k,m=1,u='^',d='v',a[]=(s+"\n\n").getBytes();for(i=0,s="";i<m;i++,s+=new String(a))for(j=0,k=0;k<a.length;k++){if(a[k]==d||a[k]==u){a[k]=(i&1<<j++)!=0?u:d;m<<=i>0?0:1;}}return s;};

        //System.out.println(f.apply("-v-"));
        System.out.println(f.apply("-v-v-v-\n-v-v-v-"));
        //System.out.println(f.apply("-v-v-v-\n-v-v-"));
        //System.out.println(f.apply("-v-v-v-v-v-\n-v-"));
        //System.out.println(f.apply("-v-v-v-v-v-\n-v-v-v-v-v-"));
    }
}

Я подумал, что «простой» способ справиться с разрывами строк, которые необходимы для правильной разметки, состоит в том, чтобы фактически повторно использовать исходный входной массив символов и заполнять его только 'v's и '^'s в соответствующих позициях.

Обновления:

Оказалось, что отсутствие сохранения позиций позволяет исключить intобъявления переменных и массива (за счет проверки каждой позиции массива, независимо от того, содержит он vили ^на лету), экономя 5 байтов.

Еще 8 байтов сохраняются путем вычисления верхнего предела (1<<numberOfSwitches)более компактно.

Согласно правилу, упомянутому в комментарии, объявление функции должно учитываться, так что теперь это лямбда-выражение ...


2
Я уверен, что вы должны включить определение функции ( String generate(String s) {...}) в ваш счетчик байтов. Вот фиксированная / лямбда-версия для 191 байта . Я немного поиграл в гольф, чтобы сбрить 3 байта
Бенджамин Уркхарт

@ BenjaminUrquhart Хорошо, это детали "правил", с которыми я не знаком (я не играю в гольф здесь, ооочень регулярно). Я думал, что фактическое { function body }должно быть актуальным, потому что не имеет значения, помещаете ли вы его в функцию, которая есть staticили нет, и, конечно, если объявление засчитывается в счет, можно преобразовать его в лямбда-выражение. Но это то, что сделано сейчас, спасибо за указание на это.
Marco13,

1
Несколько предложений: 1. используйте ascii-коды, а не chars ( d=94). 2. Инициализируйте, iкогда вы объявите это. 3. Используйте i++<mвместо отдельного приращения (необходимо изменить содержимое цикла в одном месте, но это не добавляет затрат). 4. Можете ли вы сойти с рук (i&1<<j++)>0? 5. Я не думаю, что вам нужно {}для внутреннего forцикла. 6. Вы можете заменить a[k]==d||a[k]==uна a[k]>45, я думаю. 7. Иди с j=k=0. Все это должно удалить 19 байт.
VisualMelon

@VisualMelon Некоторые из них являются подходами "классического гольфа", и некоторые из них я уже применял. Будут ли они применимы, зависит - я думаю, что некоторые из {}них необходимы, но я могу взглянуть по-другому. Однако, это a[k]>45может быть изящный трюк. По общему признанию, я только написал это, чтобы потратить некоторое время на ожидание начала собрания (отсюда и название класса - это было намеренно ;-)), но, возможно, я посмотрю по-другому - спасибо в любом случае!
Marco13

@Marco13 indeed, they are classic tricks, but all applicable here specifically. I won't spoil the fun by giving you my 172 byte solution based on them (BTW, it think yours is 192, not 191, but I don't know how the lambda counting works: I'm opposed to it in any case).
VisualMelon

3

J, 41 40 24 bytes

[:>@,@{<@(,'^'$~'v'=])"0

Try it online!


very impressive. love the use of {. although i think [:>@,@{<@(,'^'$~'v'=])"0 would be slightly more fair since "Each output board should be of the exact same format as the input" and the input isn't boxed.
Jonah

@Jonah thanks. corrected.
ngn





2

K4 , 44 байта

Решение:

-1{@[x;&w;:;]@'"v^"@a\:'!*/a:(+/w:"v"=x)#2};

Примеры:

q)k)-1{@[x;&w;:;]@'"v^"@a\:'!*/a:(+/w:"v"=x)#2}"-v-";
-v-
-^-

q)k)-1{@[x;&w;:;]@'"v^"@a\:'!*/a:(+/w:"v"=x)#2}"-v-\n-v-";
-v-
-v-
-v-
-^-
-^-
-v-
-^-
-^-

q)k)-1{@[x;&w;:;]@/:"v^"@a\:'!*/a:(+/w:"v"=x)#2}"-v-v-\n-v-v-v-\n-v-";
-v-v-
-v-v-v-
-v-
-v-v-
-v-v-v-
-^-
-v-v-
-v-v-^-
-v-
-v-v-
-v-v-^-
-^-
-v-v-
-v-^-v-
-v-
-v-v-
-v-^-v-
-^-
-v-v-
-v-^-^-
-v-
-v-v-
-v-^-^-
-^-
-v-v-
-^-v-v-
-v-
-v-v-
-^-v-v-
-^-
-v-v-
-^-v-^-
-v-
-v-v-
-^-v-^-
-^-
-v-v-
-^-^-v-
-v-
-v-v-
-^-^-v-
-^-
-v-v-
-^-^-^-
-v-
-v-v-
-^-^-^-
-^-
-v-^-
-v-v-v-
-v-
-v-^-
-v-v-v-
-^-
-v-^-
-v-v-^-
-v-
-v-^-
-v-v-^-
-^-
-v-^-
-v-^-v-
-v-
-v-^-
-v-^-v-
-^-
-v-^-
-v-^-^-
-v-
-v-^-
-v-^-^-
-^-
-v-^-
-^-v-v-
-v-
-v-^-
-^-v-v-
-^-
-v-^-
-^-v-^-
-v-
-v-^-
-^-v-^-
-^-
-v-^-
-^-^-v-
-v-
-v-^-
-^-^-v-
-^-
-v-^-
-^-^-^-
-v-
-v-^-
-^-^-^-
-^-
-^-v-
-v-v-v-
-v-
-^-v-
-v-v-v-
-^-
-^-v-
-v-v-^-
-v-
-^-v-
-v-v-^-
-^-
-^-v-
-v-^-v-
-v-
-^-v-
-v-^-v-
-^-
-^-v-
-v-^-^-
-v-
-^-v-
-v-^-^-
-^-
-^-v-
-^-v-v-
-v-
-^-v-
-^-v-v-
-^-
-^-v-
-^-v-^-
-v-
-^-v-
-^-v-^-
-^-
-^-v-
-^-^-v-
-v-
-^-v-
-^-^-v-
-^-
-^-v-
-^-^-^-
-v-
-^-v-
-^-^-^-
-^-
-^-^-
-v-v-v-
-v-
-^-^-
-v-v-v-
-^-
-^-^-
-v-v-^-
-v-
-^-^-
-v-v-^-
-^-
-^-^-
-v-^-v-
-v-
-^-^-
-v-^-v-
-^-
-^-^-
-v-^-^-
-v-
-^-^-
-v-^-^-
-^-
-^-^-
-^-v-v-
-v-
-^-^-
-^-v-v-
-^-
-^-^-
-^-v-^-
-v-
-^-^-
-^-v-^-
-^-
-^-^-
-^-^-v-
-v-
-^-^-
-^-^-v-
-^-
-^-^-
-^-^-^-
-v-
-^-^-
-^-^-^-
-^-

Объяснение:

Замена на месте "^". Определить количество комбинаций переключателей (например, 2 ^ n), сосчитать в двоичном коде, заменить переключатели ...

-1{@[x;&w;:;]@'"v^"@a\:'!*/a:(+/w:"v"=x)#2}; / the solution
-1                                         ; / print to STDOUT, swallow -1
  {                                       }  / lambda taking implicit x
                                        #2   / take 2
                             (         )     / do this together
                                  "v"=x      / does input = "v" ?
                                w:           / save as w
                              +/             / sum up
                           a:                / save as a
                         */                  / product
                        !                    / range 0..n
                    a\:'                     / convert each to base-2
               "v^"@                         / index into "v^"
             @'                              / apply each
   @[x;&w;:;]                                / apply assignment to x at indexes where w is true

2

R , 116 байт

function(x,u=utf8ToInt(x))apply(expand.grid(rep(list(c(118,94)),sum(u>45))),1,function(i)intToUtf8(`[<-`(u,u>45,i)))

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

Функция, возвращающая вектор разделенных досками новой строки


ah, I was so focused on taking input in a much harder way that I neglected the ease of this one. Nice use of "[<-"!
Giuseppe

@Giuseppe: I'm not very satisfied by this solution... but I tried to generate the combinations in other ways (e.g. using binary conversion) but this ended up being the shortest.
digEmAll


1

Retina 0.8.2, 29 bytes

T`¶v`;#
+%1`#
v$'¶$`^
%`;|$
¶

Try it online! Explanation:

T`¶v`;#

Change the newlines into ;s and the vs into # markers.

+%1`#

Replace the #s one at a time from left to right.

v$'¶$`^

Change each line into two lines, one with the # replaced with a v, one with it replaced with a ^.

%`;|$
¶

Change the ;s back into newlines and space the results apart.




1

Python 3 - construct, 203 bytes

def f(a):
 b=[0]
 for l in a.split():b+=[b[-1]+l.count('v')]
 return'\n'.join(''.join(f"{k:b}".zfill(b[-1])[x:y]+'-\n'for x,y in zip(b,b[1:]))for k in range(2**b[-1])).replace('0','-v').replace('1','-^')

Try it online!

First try, not very small but works. There is no elegant string replacement in Python...

The First loop builts a mapping of lines to bit indices, i.e. for each line, the index of the first bit in a bit counter is stored. This is used for indexing the bit counter in the next loop.

The Second loop runs a binary counter, extracts the bits for each line and iteration and joins them. After joining everything together, it is translated back to the switch map format, using string replacement.

I guess, there is a more elegant way by reusing the input string instead of rebuilding it over and over again.

Edit: inspired by the Python 3.8 answer, here is a much shorter replacing version

Python 3 - replace, 123 bytes

def f(a):r=range;n=a.count('v');return'\n'.join(a.replace('v','{}').format(*('v^'[k&2**i>0]for i in r(n)))for k in r(2**n))

Try it online!


0

Ruby, 64 bytes

Returns an array. Gets numbers from 1 to 2v (where v is the number of "v" in the input) and flips switches based on the v least significant bits. This allows us to save a byte over iterating from 0 to 2v1, because the v least significant bits in 2v are all zero.

In Ruby, i[j] returns the jth bit of i starting from the least significant bit, aka it is equivalent to (i>>j)&1.

->s{(1..2**s.count(?v)).map{|i|j=-1;s.gsub(/v/){'v^'[i[j+=1]]}}}

Try it online!


0

Charcoal, 28 bytes

⪫EX²№θv⭆θ⎇⁼λv§v^÷ιX²№…θμv붶

Try it online! Link is to verbose version of code. Explanation:

   ²                            Literal 2
  X                             Raised to power
    №                           Count of
      v                         Literal `v`
     θ                          In input string
 E                              Map over implicit range
        θ                       Input string
       ⭆                        Map over characters and join
           λ                    Current character
          ⁼                     Equal to
            v                   Literal `v`
         ⎇                      Then
              v^                Literal `v^`
             §                  Circularly indexed by
                 ι              Outer index
                ÷               Integer divided by
                   ²            Literal 2
                  X             Raised to power
                    №           Count of
                        v       Literal `v`
                      θ         In input string
                     …          Truncated to length
                       μ        Inner index
                         λ      Else current character
⪫                         ¶¶    Join with newlines
                                Implicitly print

0

PHP, 93 bytes

for(;$j<1<<$x;$j+=print$s)for($x=0,$s=$argv[1];$i=strpos($s,v,$i+1);$s[$i]=$j&1<<$x++?'^':v);

Try it online!

Standalone program, input via command line.

Loop the number of possible permutations of the input string based on the number of v's. While counting up in binary, replace each binary 1 with a ^ and each binary 0 with a v in the input string.

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