Marcel Proust ve Markov güvenlik servisinin T9 metinlerini çözüyor


11

Sanki bu meydan okuma ruhsal olarak artık Pythonesque olabilir ... Markov zincirleri veya şifreleme teknikleri hakkında önceden bilgi sahibi olmak gerekmez.

İngiliz güvenlik hizmeti M1S'den bazı önemli bilgileri edinmesi gereken bir casussunuz. M1S'nin ajanları, Wi-Fi sinyallerinin ele geçirilebileceğinin, Android / iOS güvenlik açıklarından vb. Yararlandığının farkındadır, bu nedenle hepsi T9 otomatik tamamlama kullanılarak yazılan metin bilgilerini iletmek için Nokia 3310'ları kullanır .

Daha önce istihbarat teşkilatına teslim edilmek için telefonları hacklediniz ve muhteşem plastik klavyelerinin altına tuş kaydediciler taktınız, bu yüzden şimdi yazdıkları harflere karşılık gelen sayı dizileri alıyorsunuz, böylece “ kartal yuvaları uyarıyor

84303245304270533808430637802537808430243687

Fakat bekle! Bazı T9 dizileri belirsizdir (“6263” “ad”, “yele” veya “obua” olabilir; ne kadar belirsiz olursa, o kadar şüpheli olur!), O zaman ne yaparsınız? M1S'in kullandığı tek giriş sınavının Marcel Proust'un başyapıtı “Geçmişi Anma” yı 15 saniyede özetlemek olduğunu biliyorsunuz, bu nedenle önceki şefin tümünde frekans dağılımına göre bir öncekinden sonra gelen kelimeyi seçmek istiyorsunuz. ust Proust!

Kodu kırabilir ve orijinal mesajın ne olduğunu elde edebilir misiniz?

T9 prensibi

Temsilciler tarafından kullanılan Nokia 3310 klavyeler

T9 otomatik tamamlama mekanizması aşağıdaki gibi açıklanabilir. Alfabetik karakterleri yukarıdaki resimde gösterildiği gibi sayılarla eşler.

abc     -> 2
def     -> 3
ghi     -> 4
jkl     -> 5
mno     -> 6
pqrs    -> 7
tuv     -> 8
wxyz    -> 9
<space> -> 0
<other> -> <is deleted>

T9 şifre çözücü bir dizi rakam alır ve bu tuşlara basılabilecek kelimeyi tahmin etmeye çalışır. Standart bir frekans tablosu kullanabilir, ancak bir adım daha ileri gidiyoruz ve bir Markov zinciri kullanarak bir sonraki kelimeyi tahmin ediyoruz!

Öğrenme örneği

Korpus olan Proust'un “Geçmiş Zamanı Anımsama” bu ağır soyulmuş versiyon ( s/-/ /g, s/['’]s //gve s/[^a-zA-Z ]//g- begone kafa karıştırıcı iyelik 's!) Aslen yayınlanan University of Adelaide web (bu çalışmanın metin Avustralya'da Public Domain içindedir).

Yazının tamamı tek bir uzun kelimelerin vektörü olarak, bir uzun cümle olarak, bir dize olarak analiz edilmelidir (Diliniz için daha uygun olan şekilde), satır aralıklarında elimden ve mekânlar kelimelerle bölünmüş . (Tek paragraflı bir dosya sağlamıyorum çünkü github araçları tarafından kaşlarını çatmış olabilir.)

Metnin tamamını bir dize / cümle olarak nasıl okurum? R'de bir örnek :

p_raw  <- read.table("proust.txt", sep="\t") # Because there are no tabs
p_vec  <- as.character(p_raw$V1)       # Conversion to character vector
p_str  <- paste(p_vec, collapse=" ")   # One long string with spaces
p_spl  <- strsplit(p_str, split=" ")[[1]] # Vector of 1360883 words
proust <- p_spl[p_spl!=""]           # Remove empty entries — 1360797

Görev

Sayı olarak bir basamak dizisi verildiğinde, uzun bir cümle olarak ele alınan bu eğitim metnine dayanarak bir sonraki X kelimesini tahmin etmek için bir olasılık zinciri kullanarak karşılık gelen T9 tuşları kullanılarak yazılabilecek olası bir metin dizesini döndürün .

Eğer X metnin ilk T9-kelime ve orada birden tahmin hakkın olan birini rasgele seçer aksi mümkün yalnızca birini seç.

Öncesinde w (i-1) deşifre edilmiş bir kelime ile gelen sonraki tüm T9 kelimeleri X (i ) için :

  1. Bir T9 kelimesi X , benzersiz bir şekilde normal bir x kelimesine dönüştürülebilirse , bunu yapın.
  2. X için birden fazla dönüştürme seçeneği varsa , örneğin x1, x2, ... , önceki tahmin edilen w kelimesine bakın .
    • Eğer w eşleştiren bir şey tarafından takip asla X Proust orijinal çalışmalarında, olası herhangi almak , ... x1, x2 rastgele.
    • Eğer X w hep tekabül x1 w orijinal ve hiçbir eşzamanlı vardır xi ‘s o dönüþtürülmelidir olabilir X , almak x1 .
    • Eğer X w dönüştürülebilir x1 w , x2 w ... o korpus bulunabilir, daha sonra tüm olası saymak xi ‘o takip s w ve eşlemek X korpus ve almak xi olasılığı ile xi / (x1 + x2 + ...) .

Örnek 2a. Mesaj 76630489, nerede 489olabilirse guyveya ivy(en az bir kez corpus'ta meydana gelirse), (çok muhtemel bir ilk kelime) 7663olarak deşifre edilebilir some. Hiçbir somezaman 489cesetle eşleşen hiçbir şey gelmezse, 0,5 olasılıkla guyveya ivyrastgele seçin .

Örnek 2b. İleti ise 766302277437, nerede 2277437olabilir barrierveya carrier, 7663olarak deşifre edilebilir some. Proust her zaman kullanılırsa some carrierve asla kullanılmazsa some barrier, seçin some carrier.

Örnek 2c. Diziyi deşifre etmek istediğinizi varsayalım 536307663. 5363olarak tahmin edildi lend. 7663Bunlardan herhangi biri olabilir: pond, roofve some. lendÖrnek corpus'taki sözcüğün oluşumlarını sayıyorsunuz. Diyelim ki böyle bir şey aldınız (sadece örneklemek için):

        T9  Word following lend  Occurrences
      7663  some                           7
      7663  pond                           2
      7663  roof                           1

Yani 7663öncesinde lend, orada 7/(7+2+1)=70%olasılığı 7663açılımı some% 20, pondve% 10 roof. Algoritmanız lend some% 70 vakada, lend pond% 20 vakada vb. Üretmelidir.

Temsilcilerin yalnızca az harf ve boşluk kullandığını (noktalama işaretleri yok, iyelik 'syok ve rakam yok) güvenle varsayabilirsiniz .

Ayrıca, M1S ajanlarının asla “Geçmişteki Şeyleri Anma” (29,237 kelimelik bir kelime haznesi!) Dışında herhangi bir kelime kullanmadığını varsayabilirsiniz .

T9 işlevliliği bu meydan okumada uygulandı, bu yüzden ona bir göz atabilirsiniz .

Herhangi bir yardıma ihtiyacınız olursa, olasılık zincirleri tarz içinde bakılmıştır bu , o ve aşağıdaki zorluklar, ancak daha böyle zincirlerin prensibini bilmek gerekmez: Her şey görevi belirtilmiştir.

Test senaryoları

--Inputs--
20784250276960369
20784250276960369
84303245304270533808430637802537808430243687
94280343084306289072908608430262780482737
94280343084306289072908608430262780482737

--Possible outputs--
c quick brown fox
a stick crown fox
the eagle gas left the nest blest vie agents
what did the navy pay to the coast guards
what did the navy raz un the coast guards

Kurallar:

  • Standart boşluklar geçerlidir.
  • Orijinal mesajı bilmiyorsunuz, elde ettiğiniz tek şey bir basamak dizisi ve sadece bellek / çalışma alanında / her neyse yüklemeniz gereken proust.txt dosyası . Kendi kendine yeten hiçbir şeye sahip olmaya gerek yoktur; proust.txther zaman erişilebilir olduğunu varsayalım .
  • Eğer korpusa göre birden fazla şifre çözme seçeneği mümkünse, algoritmanızın ilgili olasılıklarla farklı çıktılar üretebilmesi gerekir (bakınız Örnek 2c).

Mümkün olduğunca gizli kalmanız gerekir, böylece en kısa kod kazanır!

PS Bu olasılıksal algoritmanın bariz yararı, belirsiz bir deşifre edilmiş dize için gerçek bir orijinal dize alma olasılığının bire eğilim göstermesidir - sadece bekleyin ...

PPS Ayrıca bkz . Kısmi Eşleme ile Tahmin .


Peter Taylor’ın sanal alandaki yorumları dikkate alındı. Ne yazık ki, çok az kişi birden fazla güncellemeye rağmen orada yayınlandığı hafta boyunca cevap verdi, bu nedenle herhangi bir öneri bekliyoruz! BTW, bu benim ilk görevim!
Andreï Kostyrka

Çok fazla yanıt alamamanızın büyük bir nedeninin, bu sorunu anlamak için gereken ileri bilgilerden kaynaklandığından şüpheleniyorum. Eğer varsa edilir daha büyük bir kalabalığa hitap etmek bu zorluğu isteyen, ben daha önceki bazı iş yerinde Markov zincirini gösteren örnekler :) dahil öneriyoruz
Nathan Merrill

@NathanMerrill Tamam, örnek zorluklara 3 bağlantı ekledim. Bununla birlikte, bir kullanıcının Markov zincirlerini hiç bilmesine gerek yoktur, çünkü görev soru gövdesinde mümkün olduğunca algoritmik olarak tanımlanır: X ise, Y'yi bu öğrenme örneğinde Z hesaplanarak elde edilen olasılıklarla yapın. Olabildiğince kendi kendine yeterli yapmaya çalıştım ...
Andreï Kostyrka

Anlıyorum. Eğer açıklamazsan, kapatmak için oy verirdim. Bu sadece gelişmiş bilgiye ihtiyacı var gibi görünüyor :)
Nathan Merrill

1
Bu meydan okumayı seviyorum, ama oturup bir çözüm oluşturmak / golf oynamak için henüz vaktim olmadı. Umarım bu yakında gerçekleşir.
Mego

Yanıtlar:


1

R çözümü, neler yapılabileceğinin rakipsiz gösterimi

İlk olarak, kelime dizisini hafızaya yüklüyoruz:

p_raw  <- read.table("proust.txt", sep="\t") # Because there are no tabs
p_vec  <- as.character(p_raw$V1)       # Conversion to character vector
p_str  <- paste(p_vec, collapse=" ")   # One long string with spaces
p_spl  <- strsplit(p_str, split=" ")[[1]] # Vector of 1360883 words
proust <- p_spl[p_spl!=""]           # Remove empty entries — 1360797

İkincisi, herhangi bir metni t9 içeren bir işleve ihtiyacımız var:

t9 <- function (x) {
  x <- chartr(paste(c(letters, " "), collapse=""), "222333444555666777788899990", tolower(x))
  x <- gsub("[^0-9]", "", x, perl = TRUE) # Safety check
  x <- x[x!=""] # Also for safety because... you know...
  x
}

Sonra T9-ify Proust:

p9 <- t9(proust)

Son hazırlık: çağırdığımız bir işlevi kullanarak giriş dizesini sıfırlara böldük prep):

prep <- function (x) {
  x <- chartr("0", " ", x)
  x <- strsplit(x, " ")[[1]]
  x <- x[x!=""] # Boil the empty strings for safety
  x
}

Ve şimdi herhangi bir sayı girdi dizesini alan bir işlev öneriyorum prepve kelimeleri tek tek deşifre ediyor:

decip <- function(x, verbose = FALSE) {
  x <- prep(x)
  l <- length(x)
  decrypted <- rep(NA, l)
  tb <- table(proust[which(p9 == x[1])])
  decrypted[1] <- sample(names(tb), 1, prob=tb/sum(tb))
  if (verbose) print(decrypted[1])
  for (i in 2:l) {
    mtchl <- p9 == x[i]
    mtch <- which(mtchl)  # Positions that matched
    pmtch <- proust[mtch] # Words that matched
    tb <- table(pmtch)    # Count occurrences that matched
    if (length(tb)==1) {  # It is either 1 or >1
      decrypted[i] <- names(tb)[1]
      if (verbose) print(paste0("i = ", i, ", case 1: unique decryption"))
      } else {  # If there are more than one ways to decipher...
      preced <- proust[mtch-1] 
      s <- sum(preced==decrypted[i-1])
      if (s==0) {
        decrypted[i] <- sample(names(tb), 1)
        if (verbose) print(paste0("i = ", i, ", case 2a: multiple decryption, collocation never used, picking at random"))
        } else {
        tb2 <- table(pmtch[preced==decrypted[i-1]])
        if (length(tb2)==1) {
          decrypted[i] <-  names(tb2)[1]
          if (verbose) print(paste0("i = ", i, ", case 2b: multiple decryption, only one collocation found, using it"))
        } else {
          decrypted[i] <- sample(names(tb2), 1, prob = tb2/sum(tb2))
          if (verbose) print(paste0("i = ", i, ", case 2c: multiple decryption, ", length(tb2), " choices"))
          }
      }
    }
    if(verbose) print(decrypted[i])
  }
  decrypted
}

Ve şimdi aslında ne yapıyor:

decip("20784250276960369", verbose=TRUE)
----
[1] "a"
[1] "i = 2, case 2c: multiple decryption, 2 choices"
[1] "quick"
[1] "i = 3, case 2a: multiple decryption, collocation never used, picking at random"
[1] "brown"
[1] "i = 4, case 1: unique decryption"
[1] "fox"
[1] "a"     "quick" "brown" "fox" 

İkinci örnek:

decip("84303245304270533808430637802537808430243687", verbose=TRUE)
----
[1] "what"
[1] "i = 2, case 2b: multiple decryption, only one collocation found, using it"
[1] "did"
[1] "i = 3, case 2b: multiple decryption, only one collocation found, using it"
[1] "the"
[1] "i = 4, case 1: unique decryption"
[1] "navy"
[1] "i = 5, case 2a: multiple decryption, collocation never used, picking at random"
[1] "raz"
[1] "i = 6, case 2a: multiple decryption, collocation never used, picking at random"
[1] "um"
[1] "i = 7, case 2a: multiple decryption, collocation never used, picking at random"
[1] "the"
[1] "i = 8, case 2b: multiple decryption, only one collocation found, using it"
[1] "coast"
[1] "i = 9, case 1: unique decryption"
[1] "guards"
[1] "what"   "did"    "the"    "navy"   "raz"    "um"     "the"    "coast"  "guards"

Lütfen bunun golf oynayabileceğini yorumlamayın. Korkunç ayrıntı seviyem nedeniyle birkaç insanın bu meydan okumayla ilgilendiği anlaşılıyor, bu yüzden olası bir programın nasıl görünebileceğini göstermek için bu cevabı yayınladım. Bu cevabı yükseltmenize / düşürmenize gerek yok.


1

Python 3, 316 bayt

from random import*
from collections import*
def d(s,f):
 D=defaultdict(Counter);p=q=''
 for w in open(f).read().split():D[w.translate({97+c:(c-(c>17)-(c>24))//3+50for c in range(26)})].update([w]);D[p].update([w]);p=w
 for c in s.split('0'):q=choice([*(len(D[c])>1and D[c]&D[q]or D[c]).elements()]);print(q,end=' ')
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.