Оптимизация телефонной клавиатуры


33

Кажется, существует постоянное увлечение людьми, утомительно изучающими новые раскладки клавиатуры, такие как Dvorak или Neo, потому что это якобы делает их более производительными. Я утверждаю, что переключение раскладок клавиатуры - плохая идея, потому что вам могут понадобиться месяцы, чтобы набрать скорость, и когда вы на 5% быстрее остальных, вы облажались, если вам нужно печатать на компьютере, который не работает. твой собственный.

Кроме того, все эти люди забывают, где находится настоящее узкое место в современном общении - телефонная клавиатура.

Вот так выглядит ваша обычная телефонная клавиатура:

Телефонная клавиатура

Буква «r» - третья буква на кнопке 7; поэтому, если бы вы вводили букву «r» на мобильном телефоне, вы бы нажимали кнопку 7 три раза, для «s» вы нажимали бы ее 4 раза, а для «a» вы нажимали кнопку 2 один раз.

Учитывая это, ставить 'e' после 'd', вероятно, было плохим решением - 'e' - это наиболее часто используемая буква в английском алфавите, поэтому, если бы вы пометили кнопку 3 "EDF" вместо "DEF", вы спасло бы довольно много нажатий клавиш.

Более того, вы, вероятно, испытали на себе, что ввод 2 букв, которые имеют одну и ту же кнопку, создает неудобства - если вы хотите написать «TU», вы не можете просто нажать 8 три раза, потому что это приведет к «V». Поэтому обычно вы пишете 'T', затем нажимаете пробел, затем нажимаете клавишу Backspace, а затем пишете 'U', что равно 5 нажатию кнопок вместо 3.


TL; DR

Учитывая эти два правила:

  • Буква набирается нажатием кнопки n раз, где n - это позиция буквы на этикетке кнопки.
  • Для написания двух букв, набираемых с помощью одной и той же кнопки, требуются дополнительные нажатия двух кнопок

Какая раскладка клавиатуры телефона требует наименьшего количества нажатий кнопок, учитывая определенный текст? Вы должны использовать только кнопки 2-9, 1 и 0 зарезервированы для специальных символов.

вход

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

Выход

Я не хочу накладывать слишком много ограничений на вывод, так как это иногда дает одни языки преимущества перед другими; поэтому, однако, ваш язык показывает, что массивы в порядке, или вы можете разделить каждую метку новой строкой.

Может быть несколько возможных оптимальных макетов, вы можете распечатать любой из них. Вот простой пример:

>> echo "jackdawslovemybigsphinxofquartz" | foo.sh
ojpt
avhz
cen
skm
dyf
wbq
ixu
lgr

Бонусные очки

-35, если ваш алгоритм не использует все возможные макеты (я смотрю здесь на «перестановки» Хаскелла)

-3, если ваш код помещается в текстовое сообщение (140 символов), и вы публикуете фотографию, на которой вы отправляете код другу.

Это мой первый вызов на StackExchange. Я был бы рад услышать, нравится ли вам это или есть другие отзывы об этом!


2
Добро пожаловать на CodeGolf.SE! Я не вижу никаких проблем с вашим вопросом, но, как правило, рекомендуется сначала опубликовать ваш запрос в песочнице, чтобы получить отзывы и убрать неясности, прежде чем размещать его на основном сайте.
Мартин Эндер

Ах, это круто, я определенно буду в будущем.
Флонк

1
Мой телефон может отправлять одно 60-символьное SMS, но не поддерживает скобки должным образом, я вне бонуса?
ζ--

1
Хороший вопрос! Я не думаю, что кто-нибудь сможет избежать бонуса -35. Даже если мы ограничимся макетами, имеющими 4 символа на двух клавишах и 3 на всех оставшихся 6, существуют 26! / (2! * 6!) = 280,063,514,671,253,913,600,000 > 2^77уникальные перестановки, подсчитывающие простые перестановки клавиш только один раз.
Деннис

2
Кроме того, я спрашиваю, могли бы вы, люди, напечатать количество нажатий кнопок вашего решения. Итак, посмотрим, кто нашел лучший!
Антонио Раганьнин,

Ответы:


5

Perl, 333

$_=<>;$c{$&}++while/./g;@c=sort{$c{$b}<=>$c{$a}}keys%c;$d{$&.$1}++while/.(?=(.))/g;sub f{my$x=shift;if(my$c=pop@$x){for(grep!$_[$_],0..7){my@y = @_;$y[$_]=$c;f([@$x],@y)}}else{for(0..7){$z=$_[$_];$c+=$d{$z.$_}+$d{$_.$z}for@{$a[$_]}}$c<$m?($m=$c,@n=@_):1}}while(@c){$m= ~0;f[splice@c,0,8];push@{$a[$_]},$n[$_]for 0..7}print@$_,$/for@a

Вот попытка оптимизации для правила № 2. После моего комментария выше и вместо ответов, учитывающих это правило (см. Высокий рейтинг вопросов), я подумал, что должен кое-что приложить здесь ...

Решения, которые не оптимизированы для правила № 2, могут давать результаты, очень далекие от оптимальных. Я проверил длинный естественный текст на английском языке (на самом деле «Алиса в стране чудес»), предварительно обработанный (только строчные буквы) и, например, скрипт Perl из ответа OJW, результат был

2: ermx
3: tdfz
4: alp
5: oub
6: ick
7: nwv
8: hgj
9: syq

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

Кстати, zxqjvkbpfmygwculdrshnioateэто буквы, отсортированные по частоте, из этого текста.

Если мы попытаемся решить ее простым способом (возможно, надеясь получить бонус -35) и размещать буквы одну за другой, выбирая доступный ключ по минимальному количеству пар, мы можем закончить, например:

slbx
hdmz
nrf
iuj
ogv
awk
tcp
eyq

Я не размещаю код для этого (неправильного) решения здесь. Например, обратите внимание, cчаще, чем wи ставится на первое место. tc( ct) пары явно реже, чем ac( ca) - 43 + 235 против 202 + 355. Но затем wзаканчивается a- 598 + 88. Мы заканчиваем парами awи tc(всего 964), хотя было бы лучше acи tw(всего 635). Так далее..

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

$_=<>;                          # Read STDIN.
$c{$&}++while/./g;              # Count letters (%c hash).
@c=sort{$c{$b}<=>$c{$a}}keys%c; # Sort them by frequency, ascending
$d{$&.$1}++while/.(?=(.))/g;    # (@c array), and count pairs (%d hash).

                                # Next is recursive sub that does the job.
                                # Some CPAN module for permutations
                                # would probably do better...
                                # Arguments are reference to array of what's 
                                # left un-placed of current 8-pack of letters,
sub f{                          # and 8 element list of placed letters
    my$x=shift;                 # (or undefs).
    if(my$c=pop@$x){            # Pop a letter from 8-pack (if anything left),
        for(grep!$_[$_],0..7){  # try placing it on each available key, and 
            my@y = @_;          # call sub again passing updated arguments.
            $y[$_]=$c;
            f([@$x],@y)
        }
    }else{                      # If, OTOH, 8-pack is exhausted, find sum of
        for(0..7){              # pairs count of current permutation (@_) and 
            $z=$_[$_];          # letters placed in previous rounds (8-packs).
                                # @a is "array of arrays" - note, we didn't 
                                # have to initialize it. First "8-pack" will
                                # be placed on empty keypad "automatically".
                                # We re-use undefined (i.e. 0) $c.

            $c+=$d{$z.$_}+$d{$_.$z}for@{$a[$_]}
        }
        $c<$m                   # Is sum for current placement minimal?
            ?($m=$c,@n=@_)      # Then remember this minimum and placement.
            :1
    }
}

while(@c){
    $m= ~0;                         # Initialize "minimum" with large enough 
    f[splice@c,0,8];                # number, then call sub with each 8-pack
                                    # (and empty list of placed letters 
                                    # from current round). On return,
                                    # @n will have optimal arrangement.
    push@{$a[$_]},$n[$_]for 0..7    # Then place it permanently on keypad.
}
print@$_,$/for@a                    # Show us what you've done.

Результат:

sdfz
hlmx
nrv
iyp
ogk
acq
twb
euj

Мне не нравится эта acпара (в конце концов, Cat является одним из символов), но, тем не менее, это оптимальное расположение букв для английского, если мой код не ошибается. Не совсем «игра в гольф», просто какое-то рабочее решение, безобразное или нет.


3

Python3, это время Монте-Карло!

Чтобы решить эту проблему, я сначала посчитал, сколько «кликов» вам нужно с помощью клавиатуры по умолчанию (изначально:) abc,def,ghi,jkl,mno,pqrs,tuv,wxyz. Затем я изменяю эту клавиатуру и проверяю, дешевле ли она (текст пишется меньше кликов). Если эта клавиатура дешевле, то она становится стандартной. Я повторяю этот процесс 1Mраз.

Чтобы сменить клавиатуру, я сначала решаю, сколько изменений нужно сделать (максимальное количество изменений - это общее количество букв на клавиатуре). Затем для каждого переключателя я выбираю две кнопки и две позиции и перенесу символ из первой позиции во вторую.

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

Вывод echo "jackdawslovemybigsphinxofquartz" | python .\myscript.py:

61 ['anb', 'sef', 'hjc', 'iykl', 'odm', 'qgr', 'tuxv', 'wpz']

Где 61номер кнопки, нажатой для составления данного сообщения.

Символы (без пробелов и без комментариев): 577

Я знаю это долго, но я действительно новичок в этом деле.

from random import *
S=['abc','def','ghi','jkl','mno','pqrs','tuv','wxyz']
def P(L): # perform a switch of the keys of the keyboard:to switch from a given keyboard to another, the maximum number of exchanges is the number of the keys.
    R=randint
    N = len(''.join(L))
    W = randint(1,N)   # decide how many switches to perform
    EL = list(L)
    for i in range(0,W):
        B1=R(0,len(EL)-1)   # decide what buttons are considered in the switch
        B2=R(0,len(EL)-1)
        if len(EL[B1])==0: continue   
        P1=R(0,len(EL[B1])-1)       # decide what letter to switch and where
        if len(EL[B2])==0: P2=0
        else:   P2=R(0,len(EL[B2])-1)
        C1 = EL[B1][P1]     
        EL[B1]=EL[B1].replace(C1,'')
        EL[B2]=EL[B2][:P2]+C1+EL[B2][P2:]
    return EL
def U(L,X): # count how many clicks you need to compose the text X
    S=0
    Z=' '
    for A in X:
        for T in L:
            if A in T and Z not in T: S+=1+T.index(A)
            if A in T and Z in T: S+=3+T.index(A) # if the last character was in the same button..here the penality!
        Z=A
    return S
X=input()
n_iter=10**6
L = list(S)
cc=U(L,X)
print(cc,L)
for i in range(0,n_iter): #do some montecarlo stuff
    cc=U(L,X)
    pl=P(L)
    pc=U(pl,X)
    if(cc>pc):
        L=pl 
        print(pc,L)

Мне было так смешно, что я решил попробовать этот алгоритм с LO HOBBIT (у меня тоже есть оригинальная копия дома!). У него есть 383964буквы, и это пара кликов против клавиатуры , которую я нахожу:

909007 ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz']
879344 ['abkc', 'def', 'gqhi', 'jl', 'mno', 'rs', 'tupv', 'wxyz']
861867 ['abg', 'def', 'qhyi', 'jcl', 'mno', 'r', 'tupxv', 'swkz']
851364 ['abg', 'e', 'qchi', 'jyl', 'mn', 'dr', 'tupxv', 'sowkfz']
829451 ['ag', 'ef', 'qchi', 'jyl', 'mn', 'dbr', 'tupxv', 'sowkz']
815213 ['amg', 'ef', 'qch', 'ojyl', 'i', 'dbnr', 'tupxv', 'swkz']
805521 ['amg', 'ef', 'ch', 'ojyl', 'qi', 'dbnr', 'tupxv', 'swkz']
773046 ['amg', 'ef', 'ch', 'ojyl', 'qi', 'bnr', 'tupxv', 'dswkz']
759208 ['amg', 'eqf', 'ch', 'ojyl', 'i', 'bnr', 'tupxv', 'dswkz']
746711 ['ag', 'ekq', 'clh', 'sojy', 'bi', 'nmfr', 'tupxv', 'dwz']
743541 ['ag', 'ekq', 'clh', 'sojy', 'bi', 'nmfr', 'tpxv', 'dwuz']
743389 ['ag', 'ekq', 'clh', 'sojy', 'i', 'nmfr', 'tpxbv', 'dwuz']
734431 ['ag', 'ekq', 'lh', 'sjy', 'ci', 'nrf', 'tpxbv', 'dowumz']
705730 ['ag', 'oekq', 'lh', 'sjy', 'ci', 'nrf', 'tpxbv', 'dwumz']
691669 ['ag', 'oekq', 'lh', 'nsjy', 'ic', 'rf', 'tpxbv', 'dwumz']
665866 ['ag', 'hokq', 'el', 'nsjy', 'ic', 'rbf', 'tpxv', 'dwumz']
661610 ['agm', 'hokq', 'e', 'nsj', 'ilc', 'rbf', 'tpyxv', 'dwuz']

Поэтому я утверждаю, что эта последняя клавиатура является одной из самых практичных (с точки зрения кликов).


Как вы знаете, если это оптимально?
PyRulez

Монте-Карло не работает таким образом. Это только приближает вас к оптимальному решению. Но допустим, если за 1 миллион попыток ваше решение не изменится ... тогда вы, вероятно, используете оптимальное. (или вы недостаточно двигаетесь от этого «локального минимума»)
Антонио Раганьин

Так что для вызовов нам нужно, чтобы это работало большую часть времени?
PyRulez

1
@PyRulez Я понял, что найти оптимальное решение будет непросто (кроме случаев, когда вы попробуете все возможные решения, которые я надеялся предотвратить с помощью этого бонуса -35), поэтому я действительно очень стараюсь использовать этот подход.
Флонк

1
Интересный способ, но, может быть, эта задача не совсем его предметная область. Я проверил, и для «Алисы» клавиатура по умолчанию требует 291613 нажатий. Оптимизировано с моей программой - 195597. С подходом «Монте-Карло» я не получил менее 207000 кликов за более чем 5 миллионов итераций. И лучше поменять местами буквы, то есть 2x4 + 6x3, оставаясь неизменным.
user2846289

2

Ну, если вы просто хотите, чтобы наиболее популярные символы были назначены для бинов 2-9, Perl может сделать это за 127 символов ...

foreach(split /\s*/,<>){$x{$_}++}
foreach(sort{$x{$b}<=>$x{$a}}keys %x){$o{$n++%8}.=$_}
for(0..7){printf "%d: %s\n",$_+2,$o{$_}}

давая что-то вроде:

echo "jackdawslovemybigsphinxofquartz" | perl ./keypad.pl
2: ajeb
3: iynz
4: suv
5: ohm
6: wkl
7: rgp
8: xfc
9: dtq

Или напечатайте все это в одной строке, удалив 12 символов:

foreach(split /\s*/,<>){$x{$_}++}
foreach(sort{$x{$b}<=>$x{$a}}keys %x){$o[$n++%8].=$_}
print join",",values@o,"\n";

2
Вы можете легко обрезать это до 100 символов:$x{$_}++for split/\s*/,<>;map$o{$n++%8}.=$_,sort{$x{$b}<=>$x{$a}}keys%x;print map"$_:".$o{$_-2},2..9
ardnew

1

Хаскелл, 160 - 35 = 125

import Data.List
import GHC.Exts
main=interact f where f s=show$transpose$map($sortWith(\x->length$filter(/=x)s)['a'..'z'])[t,t.d,t.d.d,d.d.d];t=take 8;d=drop 8

Пример:

$ runhaskell % <<< "jackdaws loves my big sphinx of quartz"
["afpy","sgqz","ihr","ojt","bku","clv","dmw","enx"]
$ </usr/share/dict/propernames tr A-Z a-z | runhaskell % 
["atjx","edgq","rhb","nmp","iyv","lcf","ouw","skz"]

Можно утверждать, что это не оптимизирует для правила 2, но он помещает наиболее часто встречающиеся буквы на разные ключи.


0

JavaScript, 192 - 35 = 157

Просто заметил правило повторяющихся символов; это не учитывает это. Но, как заметил @mniip в своем ответе:

Можно утверждать, что это не оптимизирует для правила 2, но он помещает наиболее часто встречающиеся буквы на разные ключи.

o={}
a=[]
b=['','','','','','','','']
i=-1
s.split('').forEach(function(x){o[x]=o[x]?o[x]+1:1})
for(x in o)a.push([o[x],x])
a.sort().reverse().forEach(function(x){b[i=(i+1)%8]+=x[1]})
alert(b)

Вероятно, это было бы в Ruby, но я не дома и вынужден использовать Internet Explorer (eww). Но эй, иногда забавно использовать ужасные языки в игре в гольф! ;)

Пример вывода (для вашего ввода):

avlc,sukb,otj,irh,zqg,ypf,xne,wmd

Поскольку JS не имеет STDIN, программа предполагает, что ввод хранится в переменной s.


Оптимизируете ли вы это с учетом этого: «Для написания двух букв, набираемых одной и той же кнопкой, нужно дополнительно нажать 2 кнопки»
Digital Trauma

Re: последнее редактирование. Я думаю, что результат 'abcdefghia'не совсем оптимален.
user2846289

@VadimR «Вы также можете предположить, что входной текст достаточно велик, и каждая буква присутствует там хотя бы один раз»
Doorknob

Я знаю. 'azbcdefghizjklmnopqzrstuvwxyz'
user2846289

1
Вы можете оптимизировать b=['','','','','','','','']для b=[x='',x,x,x,x,x,x,x], s.split('')чтобы s.split(x)и o[x]=o[x]?o[x]+1:1в o[x]=-~o[x].
Зубная щетка

0

Python (119-35 = 84):

Предполагая, что строка является переменной a и содержит только строчные буквы:

for h in range(8): print h+2,zip(*sorted([(__import__("collections").Counter(a)[d],d) for d in set(a)])[::-1])[1][h::8]

ungolfed:

import collections

#a="jackdawslovemybigsphinxofquartz"
a=__import__("string").lowercase

b=collections.Counter(a)

c=set(a)

d=[(b[d],d) for d in c]

e=sorted(d)

f=e[::-1]

g=zip(*f)[1]

for h in range(8): print h+2,g[h::8]

PYG (76-35 = 41):

Аааа, мы можем отказаться от огромного импорта. Опять же, это предполагает, что раздетая строка находится в.

for h in R(8): print h+2,Z(*S([(CC(a)[d],d) for d in Se(a)])[::-1])[1][h::8]
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.