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


33

Здається, цей мандрівник про те, що люди втомливо вивчають нові розкладки клавіатури, як Дворак або Нео, тому що це нібито робить їх більш продуктивними. Я стверджую, що переключення макетів клавіатури - це погана ідея, тому що швидкість роботи може зайняти у вас місяці, і коли ви на 5% швидше, ніж решта, вам накрутить, якщо вам потрібно набрати на комп’ютері, який не є твій власний.

Також усі ці люди забувають, де лежить справжнє вузьке місце в сучасному спілкуванні - телефонна клавіатура.

Ось як виглядає ваша середня телефонна клавіатура:

Клавіатура телефону

Буква 'r' - третя літера на кнопці 7; тож якби ви набрали букву 'r' на мобільному телефоні, ви натискали б кнопку 7 тричі, для 's' ви натискали її 4 рази, а для 'a' ви натискали кнопку 2 один раз.

Враховуючи це, введення "e" після "d", ймовірно, було поганим рішенням - "e" - це найпоширеніша літера в англійському алфавіті, тож, якщо ви мали позначити кнопку 3 "EDF" замість "DEF", ви врятувало б досить багато натискань на клавіші.

Більше того, ви, напевно, самі переконалися, що введення двох літер, які мають одну і ту ж кнопку, викликає неприємності - якщо ви хочете написати "TU", ви не можете просто тричі натиснути 8, тому що це призведе до "V". Тому зазвичай ви пишете "T", потім натискаєте пробіл, потім натискаєте на "backspace", а потім пишете "U", що дорівнює 5 натисканням кнопок замість 3.


TL; DR

Враховуючи ці два правила:

  • Буква вводиться натисканням кнопки n разів, де n - місце, де знаходиться буква на етикетці кнопки
  • Для написання двох літер, які вводяться за допомогою однієї кнопки, потрібно додатково натиснути 2 кнопки

Який розклад клавіатури телефону вимагає найменшої кількості натискань кнопок з урахуванням конкретного тексту? Слід використовувати лише кнопки 2-9, 1 і 0, зарезервовані для спеціальних символів.

Вхідні дані

Текст, для якого ви повинні знайти оптимальний макет, подається через stdin. Вам не потрібно обробляти що-небудь, окрім малого алфавіту, і можна вважати, що вхід складається лише з цього. Ви також можете припустити, що вхідний текст досить великий, і кожна буква є хоча б раз, якщо це допомагає.

Вихідні дані

Я не хочу ставити занадто багато обмежень на вихід, оскільки це іноді дає деяким мовам переваги перед іншими; тож якби ваша мова показує масиви, це нормально, або ви можете розділити кожну мітку новим рядком.

Можливо кілька можливих оптимальних макетів, ви можете надрукувати будь-яку з них. Ось простий приклад:

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

Бонусні бали

-35, якщо ваш алгоритм не змушує всіх можливих макетів (я переглядаю "перестановки" Haskell тут)

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

Це мій перший виклик на StackExchange. Буду радий почути, чи подобається вам це, чи є якісь інші відгуки про нього!


2
Ласкаво просимо на CodeGolf.SE! Я не бачу жодних проблем з вашим запитанням, але, як правило, найкраще опублікувати виклик у пісочниці, щоб отримати зворотній зв'язок та усунути неясності перед публікацією на головному сайті.
Мартін Ендер

Ах це здорово, я точно буду в майбутньому.
Флонк

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

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

2
Також я запитую, чи могли ви люди надрукувати кількість натискань кнопок вашого рішення. Тож ми побачимо, хто знайшов найкращого!
Антоніо Рагагнін

Відповіді:


5

Перл, 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 поодинці це розорить, плюс деякі інші пари ніколи не повинні закінчуватися на одному ключі ...

Btw, 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пара (зрештою, Кіт є одним із символів), але все-таки це оптимальне розміщення літер для англійської мови, якщо мій код не помиляється. Не зовсім 'гольф' зусилля, просто якесь робоче рішення, потворне чи ні.


3

Python3, це час Монтекарло!

Щоб вирішити цю проблему, спершу підраховую, скільки потрібних вам "клацань" за допомогою клавіатури за замовчуванням (по суті:) abc,def,ghi,jkl,mno,pqrs,tuv,wxyz. Потім я модифікую цю клавіатуру і бачу, чи вона дешевша (текст пишеться менше кліків). Якщо ця клавіатура дешевша, то вона називається типовою. Я повторюю цей процес 1Mрази.

Для зміни клавіатури я спочатку вирішую, скільки змін внести (максимальна кількість змін - це загальна кількість літер n клавіатури). Потім для кожного перемикача я вибираю дві кнопки та дві позиції і переношу персонаж з першої позиції на другу.

Максимальна кількість комутаторів за раз - це кількість літер на клавіатурі, оскільки це мінімальна кількість змін, які потрібно переключити з двох повних різних клавіатур. (Я хочу, щоб завжди можна було переходити з однієї клавіатури на будь-яку іншу)

Вихід 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 залишаючись постійним.
користувач2846289

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
вдень

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)

Можливо, це було б у Рубі, але я не вдома, і мене змушують використовувати Internet Explorer (eww). Але ей, іноді весело з використанням мов, страшних у гольфі в гольф! ;)

Приклад виводу (для вашого вводу):

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

Оскільки у JS немає STDIN, програма передбачає, що вхід зберігається в змінній s.


Чи оптимізуєте ви також з цього приводу: "Для написання двох літер, які вводяться за допомогою однієї кнопки, потрібні додаткові 2 натискання кнопок"
Digital Trauma

Re: остання редакція. Я думаю, що результат для 'abcdefghia'цього не зовсім оптимальний.
користувач2846289

@VadimR "Ви також можете припустити, що вхідний текст досить великий, і кожна буква є хоча б раз"
Doorknob

Я знаю. 'azbcdefghizjklmnopqzrstuvwxyz'
користувач2846289

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

Пітон (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]

неозорений:

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):

Ааа, ми можемо кинути величезний імпорт. Знову ж таки, це передбачає, що викреслена рядок знаходиться в a.

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.