Tối ưu hóa bàn phím điện thoại


33

Dường như có một cơn sốt liên tục về những người tẻ nhạt học cách bố trí bàn phím mới như Dvorak hoặc Neo vì nó được cho là làm cho chúng hiệu quả hơn. Tôi cho rằng việc chuyển đổi bố cục bàn phím là một ý tưởng tồi, bởi vì bạn có thể mất nhiều tháng để tăng tốc và khi bạn nhanh hơn 5% so với phần còn lại, bạn sẽ bị rối nếu bạn cần gõ trên máy tính không của riêng bạn

Ngoài ra, tất cả những người này quên nơi tắc nghẽn thực sự trong giao tiếp hiện đại - bàn phím điện thoại.

Đây là cách bàn phím điện thoại trung bình của bạn trông như thế nào:

Bàn phím điện thoại

Chữ 'r' là chữ cái thứ ba trên nút 7; vì vậy, nếu bạn nhập chữ 'r' trên điện thoại di động, bạn sẽ nhấn nút 7 ba lần, vì 'bạn' bạn nhấn nó 4 lần và cho 'a' bạn nhấn nút 2 một lần.

Xem xét điều này, đặt 'e' sau 'd' có lẽ là một quyết định tồi - 'e' là chữ cái được sử dụng phổ biến nhất trong bảng chữ cái tiếng Anh, vì vậy, nếu bạn đặt nhãn 3 "EDF" thay vì "DEF", bạn sẽ tiết kiệm khá nhiều tổ hợp phím.

Hơn nữa, có lẽ bạn đã từng trải qua việc gõ 2 chữ cái có chung nút là một điều phiền toái - nếu bạn muốn viết "TU", bạn không thể nhấn 8 ba lần, vì điều đó sẽ dẫn đến 'V'. Vì vậy, thông thường bạn sẽ viết 'T', sau đó nhấn phím cách, sau đó nhấn backspace và sau đó viết 'U', bằng 5 lần nhấn nút thay vì 3.


TL; DR

Đưa ra hai quy tắc sau:

  • Một chữ cái được gõ bằng cách nhấn nút n lần, trong đó n là vị trí của chữ cái trên nhãn của nút
  • Viết hai chữ cái được gõ bằng cùng một nút yêu cầu thêm 2 lần nhấn nút

Cách bố trí bàn phím điện thoại yêu cầu số lần nhấn nút ít nhất, được cung cấp một văn bản cụ thể? Bạn chỉ nên sử dụng các nút 2-9, 1 và 0 được dành riêng cho các ký hiệu đặc biệt.

Đầu vào

Văn bản mà bạn nên tìm bố cục tối ưu được cung cấp qua stdin. Bạn không cần phải xử lý bất cứ điều gì khác ngoài bảng chữ cái chữ thường và có thể cho rằng đầu vào chỉ bao gồm điều đó. Bạn cũng có thể giả định rằng văn bản đầu vào lớn một cách hợp lý và mỗi chữ cái đều ở đó ít nhất một lần, nếu điều đó có ích.

Đầu ra

Tôi không muốn đặt quá nhiều ràng buộc cho đầu ra, vì điều đó đôi khi mang lại một số lợi thế về ngôn ngữ so với các ngôn ngữ khác; do đó, tuy nhiên ngôn ngữ của bạn hiển thị các mảng là tốt, thay vào đó, bạn có thể tách riêng từng nhãn bằng một dòng mới.

Có thể có nhiều bố cục tối ưu có thể, bạn có thể in bất kỳ một trong số chúng. Đây là một ví dụ đơn giản:

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

Điểm thưởng

-35 nếu thuật toán của bạn không bắt buộc tất cả các bố cục có thể (Tôi đang xem 'hoán vị' của Haskell ở đây)

-3 nếu mã của bạn vừa với tin nhắn văn bản (140 ký tự) và bạn đăng một bức ảnh bạn gửi mã cho bạn bè.

Đây là thử thách đầu tiên của tôi trên StackExchange. Tôi rất vui khi biết bạn thích nó hay có bất kỳ phản hồi nào khác về nó!


2
Chào mừng bạn đến với CodeGolf.SE! Tôi không thấy bất kỳ vấn đề nào với câu hỏi của bạn, nhưng nói chung nên gửi thử thách của bạn trong hộp cát trước để nhận một số phản hồi và xóa bỏ sự mơ hồ trước khi đăng lên trang web chính.
Martin Ender

Ah thật tuyệt, tôi chắc chắn sẽ có trong tương lai.
Flonk

1
Điện thoại của tôi có thể gửi một tin nhắn SMS 60 ký tự, nhưng không hỗ trợ dấu ngoặc đúng cách, tôi có hết tiền thưởng không?
ζ--

1
Câu hỏi hay! Tôi không nghĩ ai sẽ có thể tránh được phần thưởng -35. Ngay cả khi chúng ta hạn chế bố trí có 4 ký tự trên hai trong số các phím và 3 trên tất cả 6 ký tự còn lại, vẫn có 26! / (2! * 6!) = 280,063,514,671,253,913,600,000 > 2^77các hoán vị duy nhất, chỉ tính các sắp xếp lại đơn giản của các phím một lần.
Dennis

2
Ngoài ra, tôi hỏi nếu mọi người có thể in số lần nhấn nút của giải pháp của bạn. Vì vậy, chúng tôi sẽ xem ai tìm thấy tốt nhất!
Antonio Ragagnin

Câu trả lời:


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

Đây là một nỗ lực để tối ưu hóa cho quy tắc # 2. Sau khi nhận xét của tôi, ở trên và thay cho các câu trả lời có tính đến quy tắc đó (xem xếp hạng câu hỏi cao), tôi nghĩ rằng tôi nợ một số nỗ lực ở đây ...

Các giải pháp không tối ưu hóa cho quy tắc số 2 có thể tạo ra đầu ra rất xa so với tối ưu. Tôi đã kiểm tra văn bản tiếng Anh tự nhiên dài (thực sự là "Alice ở xứ sở thần tiên"), được xử lý trước (chỉ chữ thường) và ví dụ: tập lệnh Perl từ câu trả lời của OJW, kết quả là

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

er một mình phá hỏng nó, cộng với một số cặp khác không bao giờ nên kết thúc trên cùng một khóa ...

Btw, zxqjvkbpfmygwculdrshnioatelà các chữ cái được sắp xếp, tần số tăng dần, từ văn bản đó.

Nếu chúng tôi cố gắng giải quyết một cách dễ dàng (hy vọng có thể nhận được -35 phần thưởng) và đặt từng chữ cái một, chọn khóa có sẵn bằng cách đếm số lượng tối thiểu theo cặp, chúng tôi có thể kết thúc bằng:

slbx
hdmz
nrf
iuj
ogv
awk
tcp
eyq

Tôi không đăng mã cho giải pháp (sai) này ở đây. Ví dụ, lưu ý, clà thường xuyên hơn wvà được đặt đầu tiên. tc( ct) các cặp rõ ràng là ít thường xuyên hơn ac( ca) - 43 + 235 so với 202 + 355. Nhưng sau đó wkết thúc với a- 598 + 88. Chúng tôi kết thúc với các cặp awtc(tổng cộng 964), mặc dù nó sẽ tốt hơn actw(tổng cộng 635). V.v ...

Vì vậy, thuật toán tiếp theo cố gắng kiểm tra 8 chữ cái còn lại (hoặc 2, nếu cuối cùng) so với các chữ cái thường có trên bàn phím và để đặt chúng sao cho số lượng cặp đôi là tối thiểu.

$_=<>;                          # 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.

Kết quả là:

sdfz
hlmx
nrv
iyp
ogk
acq
twb
euj

Tôi không thích accặp này (Rốt cuộc, Mèo là một trong những nhân vật), nhưng, vẫn là vị trí chữ cái tối ưu cho tiếng Anh, nếu mã của tôi không sai. Không chính xác nỗ lực 'chơi gôn', chỉ là một số giải pháp làm việc, xấu hay không.


3

Python3, đó là thời gian Montecarlo!

Để giải quyết vấn đề này, trước tiên tôi đếm số lượng "lần nhấp" bạn cần với bàn phím mặc định (về cơ bản abc,def,ghi,jkl,mno,pqrs,tuv,wxyz:). Sau đó, tôi sửa đổi bàn phím này và xem nó có rẻ hơn không (văn bản được viết bằng ít lần nhấp chuột hơn). Nếu bàn phím này rẻ hơn, thì nó trở thành bàn phím mặc định. Tôi lặp lại quá trình này 1Mlần.

Để thay đổi bàn phím, trước tiên tôi quyết định sẽ thực hiện bao nhiêu thay đổi (số lượng thay đổi tối đa là tổng số chữ cái n của bàn phím). Sau đó, với mỗi chuyển đổi, tôi chọn hai nút và hai vị trí và tôi chuyển một ký tự từ vị trí đầu tiên sang vị trí thứ hai.

Số lượng công tắc tối đa mỗi lần là số lượng chữ cái trong bàn phím vì đây là số lượng thay đổi tối thiểu bạn cần để chuyển từ hai bàn phím hoàn chỉnh khác nhau. (Tôi muốn luôn có thể chuyển từ bàn phím này sang bàn phím khác)

Đầu ra của echo "jackdawslovemybigsphinxofquartz" | python .\myscript.pylà:

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

Ở đâu 61 là số nút của ép để soạn một thông điệp nhất định.

Ký tự (không có khoảng trắng và không có nhận xét): 577

Tôi biết nó lâu nhưng tôi thực sự mới với những thứ này.

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)

Tôi thấy thật buồn cười khi tôi quyết định thử thuật toán này với LO HOBBIT (tôi cũng có một bản gốc ở nhà!). Nó có 383964các chữ cái và đây là vài lần nhấpbàn phím mà tôi đang tìm:

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']

Vì vậy, tôi khẳng định cái cuối cùng này là một trong những bàn phím thiết thực nhất (về số lần nhấp).


Làm thế nào để bạn biết nếu nó là tối ưu?
PyRulez

Montecarlo không hoạt động theo cách này. Nó chỉ đưa bạn đến gần và gần hơn với giải pháp tối ưu. Nhưng giả sử, nếu trong 1 triệu lần thử, giải pháp của bạn không thay đổi..vì có lẽ bạn đang sử dụng giải pháp tối ưu. (hoặc bạn không di chuyển đủ từ "mức tối thiểu cục bộ" này)
Antonio Ragagnin

Vì vậy, đối với những thách thức, chúng ta chỉ cần nó để làm việc hầu hết thời gian sau đó?
PyRulez

1
@PyRulez Tôi nhận ra rằng đây sẽ không phải là một vấn đề dễ dàng để tìm một giải pháp tối ưu cho (trừ khi bạn thử tất cả các giải pháp có thể, mà tôi hy vọng sẽ ngăn chặn với phần thưởng -35 đó), vì vậy tôi thực sự thực sự đào sâu phương pháp này.
Flonk

1
Phương pháp thú vị, nhưng có lẽ nhiệm vụ này không chính xác là miền của nó. Tôi đã kiểm tra và, đối với 'Alice', bàn phím mặc định cần 291613 lần nhấp. Tối ưu hóa với chương trình của tôi - 195597. Với phương pháp 'Monte Carlo', tôi đã không nhận được ít hơn 207000 lần nhấp trong hơn 5 triệu lần lặp. Và, tốt hơn là trao đổi các chữ cái, tức là bố cục 2x4 + 6x3 không đổi.
dùng2846289

2

Chà, nếu bạn chỉ muốn các nhân vật phổ biến nhất được gán cho các thùng 2-9, Perl có thể làm điều đó trong 127 ký tự ...

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

đưa ra một cái gì đó như:

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

Hoặc in tất cả trên một dòng, xóa 12 ký tự:

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

2
bạn có thể dễ dàng cắt nó thành 100 ký tự:$x{$_}++for split/\s*/,<>;map$o{$n++%8}.=$_,sort{$x{$b}<=>$x{$a}}keys%x;print map"$_:".$o{$_-2},2..9
ardew 23/03 '

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

Thí dụ:

$ 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"]

Người ta có thể lập luận rằng điều này không tối ưu hóa cho quy tắc 2, nhưng nó đặt hầu hết các chữ cái thường xuyên trên các khóa khác nhau .


0

JavaScript, 192 - 35 = 157

Chỉ cần chú ý quy tắc nhân vật lặp lại; điều này không tính đến điều đó. Nhưng như @mniip lưu ý trong câu trả lời của mình:

Người ta có thể lập luận rằng điều này không tối ưu hóa cho quy tắc 2, nhưng nó đặt hầu hết các chữ cái thường xuyên trên các khóa khác nhau .

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)

Điều này có thể đã có trong Ruby, nhưng tôi không ở nhà và bị buộc phải sử dụng Internet Explorer (eww). Nhưng này, đôi khi thật thú vị khi sử dụng những ngôn ngữ khủng khiếp khi chơi golf! ;)

Đầu ra mẫu (cho đầu vào của bạn):

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

Vì JS không có STDIN, chương trình giả định rằng đầu vào được lưu trữ trong biến s.


Bạn có đang tối ưu hóa điều này trong đầu không: "Viết hai chữ cái được gõ bằng cùng một nút yêu cầu thêm 2 lần nhấn nút"
Digital Trauma

Re: chỉnh sửa lần cuối. Tôi nghĩ rằng đầu ra cho 'abcdefghia'là không chính xác tối ưu.
dùng2846289

@VadimR "Bạn cũng có thể giả sử rằng văn bản đầu vào khá lớn và mỗi chữ cái đều ở đó ít nhất một lần"
Doorknob

Tôi biết. 'azbcdefghizjklmnopqzrstuvwxyz'
dùng2846289

1
Bạn có thể tối ưu hóa b=['','','','','','','','']để b=[x='',x,x,x,x,x,x,x], s.split('')đến s.split(x)o[x]=o[x]?o[x]+1:1đến o[x]=-~o[x].
Bàn chải đánh răng

0

Con trăn (119-35 = 84):

Giả sử chuỗi là một biến a và chỉ chứa các chữ cái viết thường:

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]

vô dụng:

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, chúng ta có thể bỏ nhập khẩu tuyệt vời. Một lần nữa, điều này giả sử chuỗi bị tước nằm trong a.

for h in R(8): print h+2,Z(*S([(CC(a)[d],d) for d in Se(a)])[::-1])[1][h::8]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.