优化电话键盘


33

人们似乎在不断地热衷于学习乏味的新键盘布局(例如Dvorak或Neo),因为它据说可以提高他们的生产力。我认为切换键盘布局不是一个好主意,因为它可能需要几个月的时间来加快速度,并且最终速度比其他键盘快5%时,如果您需要在不是不是你自己的。

而且,所有这些人都忘记了现代通信中真正的瓶颈在哪里-电话键盘。

这是普通电话键盘的外观:

电话键盘

字母“ r”是按钮7上的第三个字母;因此,如果您要在手机上键入字母“ r”,则需按3次按钮7,对于“ s”,请按4次,对于“ a”,请按一次2。

考虑到这一点,将“ e”放在“ d”之后可能是一个错误的决定-“ e”是英语字母表中最常用的字母,因此,如果将按钮3标记为“ EDF”而不是“ DEF”,则表示可以节省很多击键。

此外,您可能会自己经历过键入两个共享相同按钮的字母是很麻烦的-如果您想写“ TU”,则不能只敲8次三遍,因为那样会导致'V'。因此,通常您会写“ T”,然后按空格键,然后按退格键,然后写“ 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!我看不到您的问题有任何问题,但通常最好先在沙箱中发布您的挑战,以获取一些反馈并消除歧义,然后再发布到主网站上。
Martin Ender 2014年

啊,太酷了,我以后肯定会的。
Flonk

1
我的电话只能发送一个60个字符的短信,但不能正确地使用括号,我是否没有花红?
ζ--

1
好问题!我认为没有人能够避免 -35奖金。即使我们将布局限制为两个键上有4个字符,其余所有6个上具有3个字符的布局,也存在26! / (2! * 6!) = 280,063,514,671,253,913,600,000 > 2^77唯一的排列,仅对键的简单重排计数一次。
丹尼斯

2
另外,我问你们是否可以打印解决方案的按钮按下次数。因此,我们将看看谁找到了最好的人!
Antonio Ragagnin 2014年

Answers:


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进行优化的解决方案可能会产生远非最佳的输出。我检查了很长的自然英文文本(实际上是“爱丽丝梦游仙境”),经过预处理(仅小写字母),例如,OJW回答中的Perl脚本,结果是

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

我不在此发布此(错误)解决方案的代码。例如,请注意,cw第一个更频繁。tcct)对的出现频率明显低于acca)-43 + 235对202 + 355。但最后wa598 + 88 结尾。我们最终用对awtc(964个),虽然这将是更好的actw(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时间。

要更改键盘,我首先确定要进行的更改(更改的最大数量是键盘中字母的总数)。然后,对于每个开关,我选择两个按钮和两个位置,然后将字符从第一个位置转移到第二个位置。

每次最多可切换的次数是键盘中字母的数目,因为它是您需要从两个完全不同的键盘切换的最少变更次数。(我希望始终可以从一个键盘切换到其他键盘)

输出 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 2014年

蒙特卡洛无法以这种方式工作。它只会使您越来越接近最佳解决方案。但是可以说,如果一百万次尝试不改变您的解决方案,那么您可能正在使用最佳解决方案。(或者您没有从这个“局部最小值”
移开

因此,对于挑战,我们只需要它在大多数时间都可以工作吗?
PyRulez 2014年

1
@PyRulez我意识到,要找到最佳解决方案并不是一个容易的问题(除非您尝试所有可能的解决方案,我希望使用-35奖金来防止这种情况),所以实际上我确实在研究这种方法。
Flonk

1
有趣的方法,但也许此任务并不完全是它的领域。我检查了一下,对于“爱丽丝”,默认键盘需要291613次点击。使用我的程序进行了优化-195597。使用“蒙特卡洛”方法时,在超过500万次迭代中,我的点击次数并没有少于207000次。并且,最好交换字母,即2x4 + 6x3布局保持不变。
user2846289 2014年

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 2014年

1

Haskell,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,因此程序假定输入存储在variable中s


您是否也在考虑这一点呢?“使用同一个按钮输入两个字母需要
Digital Trauma 2014年

回复:最后编辑。我认为的输出'abcdefghia'并非完全最佳。
user2846289 2014年

@VadimR“您还可以假定输入文本相当大,每个字母至少在其中一次”
Doorknob

我知道。'azbcdefghizjklmnopqzrstuvwxyz'
user2846289 2014年

1
您可以优化b=['','','','','','','','']b=[x='',x,x,x,x,x,x,x]s.split('')s.split(x)o[x]=o[x]?o[x]+1:1o[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]

松散:

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

Aaah,我们可以放弃大笔的进口。同样,这假定剥离的字符串在a中。

for h in R(8): print h+2,Z(*S([(CC(a)[d],d) for d in Se(a)])[::-1])[1][h::8]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.