Идеальные номерные знаки
Начав несколько лет назад, я немного поиграл, катаясь на машине: проверяя, являются ли соседние номерные знаки «идеальными». Это относительно редко, но захватывающе, когда вы его найдете.
Чтобы проверить, является ли номерной знак идеальным:
- Суммируйте символы: A = 1, B = 2, ... Z = 26.
- Возьмите каждый последовательный кусок цифр и сложите их; умножьте каждую из этих сумм вместе.
Если значения в части 1 и части 2 равны, поздравляем! Вы нашли идеальный номерной знак!
Примеры
License plate: AB3C4F
Digits -> 3 * 4
= 12
Chars -> A + B + C + F
= 1 + 2 + 3 + 6
= 12
12 == 12 -> perfect!
License plate: G34Z7T
Digits -> (3 + 4) * 7
= 49
Chars -> G + Z + T
= 7 + 26 + 20
= 53
49 != 53 -> not perfect!
License plate: 10G61
Digits -> (1 + 0) * (6 + 1)
= 7
Chars -> G
= 7
7 == 7 -> perfect!
Соревнование
Я использовал номерные знаки длиной 5 и 6 в качестве примеров, но эта процедура действительна для любой длины номерного знака. Ваша задача для заданной длины N вернуть количество идеальных номерных знаков этой длины. Для целей конкурса действительный номерной знак представляет собой любую комбинацию цифр 0-9 и символов AZ. Табличка должна содержать как символ, так и цифру, чтобы считаться потенциально совершенной. Для проверки, вот значения, которые я получил (хотя я не могу быть на 100% об их правильности, хахаха)
N < 2: 0
N = 2: 18
N = 3: 355
N = 4: 8012
Заметки
Если каким-то образом это упрощает проблему на вашем языке, вы можете вывести пропорцию идеальных номерных знаков для данного N, по крайней мере, до 2 значащих цифр.
N < 2: 0
N = 2: 0.0138888...
N = 3: 0.0076088...
N = 4: 0.0047701...
ИЛИ, вы можете вывести эквивалентное значение мод 256
N < 2: 0
N = 2: 18
N = 3: 99
N = 4: 76
Кратчайшие победы!
N
.Ответы:
Python 3.6, 150 байт
полученные результаты:
Неуправляемая версия с объяснением:
Проблема сводится к поиску дерева, в котором каждый уровень дерева соответствует позиции в номере автомобильного номера, а каждый узел имеет 36 дочерних элементов (10 цифр и 26 букв). Функция выполняет рекурсивный поиск дерева, накапливая значения для цифр и букв в процессе работы.
Гольф включен, преобразование циклов for в суммы генераторов:
Затем объединяем генераторы. Кодируйте буквы от A до Z от -1 до -26 и цифры от 0 до 9. Таким образом, сумма становится:
где args это:
Остальная часть игры в гольф - это преобразование функции в лямбду, сокращение имен переменных и упрощение выражений.
источник
n*n*log(n)
или что-то подобное?Дьялог АПЛ,
5756 байт(предполагается, что
⎕io←0
)a
матрица всех действующих номерных знаков (кроме00...0
), закодированная: 0-9 для цифр, 10-35 для буквb
битовая маска для того, где встречаются цифрыc
битовая маска для последней цифры в каждой группе последовательных цифристочник
Python 2,
359295 байтДовольно долго; это тривиальное решение. Я уверен, что это правильно, хотя это не соответствует контрольным случаям в тесте. Решения, которые я получаю, соответствуют ответам Дада.
-64 байта благодаря предложениям от @numbermaniac
источник
for
; междуmap(ord,x)
иif
; и в последней строке, между.join(x)
иfor
. Я думаю, вы также можете сэкономить еще больше, если переопределите функции в лямбдах.Python 2 ,
291287276273 байтаПопробуйте онлайн!
Полученные результаты:
источник
Perl 5 , 117 байт
116 байт кода +
-p
флаг.Попробуйте онлайн!
Это кажется довольно неоптимальным, но у меня сейчас нет идей.
Сам код очень неэффективен, поскольку он вычисляет каждую перестановку
a..z,0..9
длиныn
(это занимает примерно 1 секундуn=3
, ~ 15 секундn=4
и ~ 7 минутn=5
).Алгоритм довольно прост: для каждой возможной таблички размера
n
(сгенерированной сglob"{@F}"x$_
-glob
оператор довольно волшебен)$r*=eval s/./+$&/gr for/\d+/g;
вычисляет произведение каждого куска цифр и$r+=64-ord for/\pl/g
вычитает из него вес букв. Затем мы увеличиваем счетчик,$\
если$r
is0
(!$r
) и если табличка содержит цифры и буквы (/\pl/*/\d/
).$\
неявно печатается в конце благодаря-p
флажку.Обратите внимание , что номер я получаю является
n=2 -> 18
,n=3 -> 355
,n=4 -> 8012
,n=5 -> 218153
. Я уверен, что это правильные, но я могу ошибаться, в этом случае дайте мне знать, и я удалю этот ответ.источник
APL (Dyalog) , 71 байт
Полное тело программы. Подсказки для N. N≥4 требуют огромных объемов памяти и вычислений.
Попробуйте онлайн!
источник
Scala, 265 байт
Пояснения:
Заметки :
-64
и-48
используются для преобразованияChar
(соответственно буквы и цифры) , егоInt
значение ('A' - 64 = 1
,'B' - 64 = 2
...,'9' - 48 = 9
)l.split("[A-Z]").filter(""<)
используется для исключения""
значений, еслиl
начинается с буквы (пример:)"A1".split("[A-Z]") => Array("", 1)
. Там может быть лучшее и более короткое решениеТестовые случаи:
Полученные результаты :
Функция довольно медленная,
n > 4
поскольку все комбинации должны быть сгенерированы.источник
Java,
382365 байтGolfed
Детальнее
источник
n
качестве входных данных.int h(String s){int m=0;for(int c:s.toCharArray())m+=c-48;return m;}int g(String t){int d=1,c=0;for(String s:t.split("[^0-9]"))d*=h(s);for(String s:t.split("[^A-Z]"))c+=s.charAt(0)-65;return d==c?1:0;}int f(String t,int n){int m=0;if(t.length()==n)return g(t);for(int d=48;d<58;)m+=f(t+d++,n);for(int c=65;c<91;)m+=f(t+c++,n);return m;}int s(int n){return f("",n);}
( 365 байт ) Вы можете сравнить свою текущую версию с этой, чтобы увидеть изменения, которые я сделал (слишком много, чтобы поместиться в оставшейся части этого комментария). :)GAP , 416 байт
Не выиграет от размера кода, и далеко от постоянного времени, но использует математику для ускорения!
Чтобы выжать ненужные пробелы и получить одну строку с 416 байтами, пройдите через это:
Мой старый ноутбук, «разработанный для Windows XP», может рассчитать
f(10)
менее чем за одну минуту и пойти гораздо дальше в течение часа:Как это работает
Предположим, что сначала мы хотим узнать только количество идеальных номерных знаков, соответствующих шаблону
LDDLLDL
, гдеL
обозначает букву иD
цифру. Предположим, у нас есть списокl
чисел, которыйl[i]
дает количество способов, которыми буквы могут дать значениеi
, и аналогичный списокd
для значений, которые мы получаем из цифр. Тогда число совершенных номерных знаков с общей стоимостьюi
будет справедливымl[i]*d[i]
, и мы получим количество всех совершенных номерных знаков с нашим шаблоном, суммируя это по всемi
. Давайте обозначим операцию получения этой суммыl@d
.Теперь, даже если лучший способ получить эти списки - это попробовать все комбинации и сосчитать, мы можем сделать это независимо для букв и цифр, рассматривая
26^4+10^3
случаи вместо26^4*10^3
случаев, когда мы просто пробегаем все таблички, соответствующие шаблону. Но мы можем сделать намного лучше:l
это просто список коэффициентов,(x+x^2+...+x^26)^k
гдеk
число букв, здесь4
.Точно так же мы получаем количество способов получить сумму цифр в серии
k
цифр как коэффициенты(1+x+...+x^9)^k
. Если имеется более одного набора цифр, нам нужно объединить соответствующие списки с операциейd1#d2
, вi
которой в качестве значения в качестве суммы принимается сумма всехd1[i1]*d2[i2]
где . Вместе с тем, что он является билинейным, это дает хороший (но не очень эффективный) способ его вычисления.i1*i2=i
. Это свертка Дирихле, которая является просто произведением, если мы интерпретируем списки как коэффициенты рядов Дирхлета. Но мы уже использовали их как полиномы (конечные степенные ряды), и нет хорошего способа интерпретировать операцию для них. Я думаю, что это несоответствие является частью того, что затрудняет поиск простой формулы. Давайте все равно будем использовать его на полиномах и использовать те же обозначения#
. Легко вычислить, когда один операнд является мономом: мы имеемp(x) # x^k = p(x^k)
Обратите внимание, что
k
буквы дают значение самое большее26k
, в то время какk
однозначные числа могут давать значение9^k
. Таким образом, мы часто получим ненужные высокие полномочия вd
полиноме. Чтобы избавиться от них, мы можем вычислить по модулюx^(maxlettervalue+1)
. Это дает большую скорость и, хотя я не сразу заметил, даже помогает в гольф, потому что теперь мы знаем, что степеньd
не больше, чемl
, что упрощает верхний предел в финалеSum
. Мы получаем еще большее ускорение, выполняяmod
вычисления в первом аргументеValue
(см. Комментарии), а выполнение всего#
вычисления на более низком уровне дает невероятное ускорение. Но мы все еще пытаемся быть законным ответом на проблему игры в гольф.Итак, мы получили
l
иd
и их можно использовать для вычисления количества совершенных номерных знаков с рисункомLDDLLDL
. Это тот же номер, что и для шаблонаLDLLDDL
. В общем, мы можем изменить порядок серий цифр разной длины, сколько захотим,NrArrangements
дает количество возможностей. И хотя между рядами цифр должна быть одна буква, остальные буквы не являются фиксированными.Binomial
Считает эти возможности.Теперь осталось пройти через все возможные способы получения длин серийных цифр.
r
пробегает все числа пробегов,c
все общее количество цифр иp
все разделыc
сr
слагаемыми.Общее количество разделов, на которые мы смотрим, на два меньше, чем количество разделов
n+1
, и функции разделов растут примерно такexp(sqrt(n))
. Таким образом, хотя все еще существуют простые способы улучшить время выполнения путем повторного использования результатов (работа с разделами в другом порядке), для фундаментального улучшения нам следует избегать отдельного рассмотрения каждого раздела.Быстро вычислить
Обратите внимание, что
(p+q)@r = p@r + q@r
. Само по себе это просто помогает избежать некоторых умножений. Но вместе с(p+q)#r = p#r + q#r
этим означает, что мы можем объединить путем простого сложения полиномы, соответствующие разным разбиениям. Мы не можем просто добавить их все, потому что нам все еще нужно знать, с какимиl
нам нужно@
объединить, какой фактор мы должны использовать, и какие#
расширения все еще возможны.Давайте объединим все полиномы, соответствующие разделам с одинаковой суммой и длиной, и уже учтем несколько способов распределения длин серий цифр. В отличие от того, что я размышлял в комментариях, мне не нужно заботиться о наименьшем используемом значении или о том, как часто оно используется, если я буду уверен, что не буду расширять это значение.
Вот мой код C ++:
Это использует библиотеку GNU MP. На Debian установите
libgmp-dev
. Компилировать сg++ -std=c++11 -O3 -o pl pl.cpp -lgmp -lgmpxx
. Программа берет свой аргумент из стандартного ввода. Для выбора времени используйтеecho 100 | time ./pl
.В конце
a[sum][length][i]
приводится количество способов, которымиsum
цифры вlength
прогонах могут дать номерi
. Во время вычислений, в началеm
цикла, он дает число способов, которые могут быть выполнены с числами, превышающимиm
. Все начинается сa[0][0][1]=1
. Обратите внимание, что это расширенный набор чисел, которые нам нужны для вычисления функции для меньших значений. Таким образом, почти одновременно мы можем вычислить все значения доn
.Нет рекурсии, поэтому у нас есть фиксированное количество вложенных циклов. (Самый глубокий уровень вложенности равен 6.) Каждый цикл проходит через ряд значений, линейных в
n
худшем случае. Так что нам нужно только полиномиальное время. Если мы посмотрим ближе на вложенныеi
иj
циклыextend
, мы найдем верхний предел дляj
формыN/i
. Это должно дать только логарифмический коэффициент дляj
цикла. Внутренняя петля вf
(с иsumn
т. Д.) Похож. Также имейте в виду, что мы вычисляем с быстро растущими числами.Обратите внимание, что мы храним
O(n^3)
эти числа.Экспериментально я получаю эти результаты на разумном оборудовании (i5-4590S):
f(50)
требуется одна секунда и 23 МБ,f(100)
требуется 21 секунда и 166 МБ,f(200)
требуется 10 минут и 1,5 ГБ иf(300)
один час и 5,6 ГБ. Это предполагает сложность времени лучше, чемO(n^5)
.источник
n=5
, нет случай с двумя цифрами и двумя однозначными числами, потому что тогда у нас недостаточно букв для разделения чисел. Вот что делают три внешнихfor
цикла: проходят через все полезные разделы чисел<n
. (И я только что понял, что я также допускаюn
цифры. По счастливой случайности другая оптимизация считает это 0).<n/2
, все разделы полезны. А оставшиеся вычисления все еще занимают непостоянное время. Чтобы увидеть, что происходит, вы можете добавитьPrint(p,"\n");
в начало телаfor p...
цикла. - У меня появилась идея использовать на один цикл меньше, но это поможет только размеру кода.mod
(что уже очень помогло) вValue
, изменив его наValue(d mod x^(1+QuoInt(s(l)-1,i-1)),x^(i-1))
. Одно это позволяет вычислитьf(15)
за 80 секунд.Pyth, 55 байтов
источник