Marcel Proust và Markov giải mã các văn bản T9 của dịch vụ bảo mật


11

Như thể thách thức này có thể là tinh thần Pythonesque hơn nữa ... Không cần có kiến ​​thức trước về chuỗi Markov hoặc kỹ thuật mã hóa.

Bạn là một điệp viên cần lấy một số thông tin quan trọng từ dịch vụ bảo mật M1S của Anh. Các nhân viên của M1S nhận thức rõ rằng tín hiệu Wi-Fi của họ có thể bị chặn, các lỗ hổng bảo mật Android / iOS của họ bị khai thác, v.v., vì vậy tất cả đều sử dụng Nokia 3310 để truyền thông tin văn bản được nhập bằng tính năng tự động hoàn thành T9 .

Trước đây bạn đã hack điện thoại để giao cho cơ quan tình báo và đã cài đặt keylogger dưới bàn phím nhựa vinh quang của họ, vì vậy bây giờ bạn nhận được chuỗi số tương ứng với các chữ cái họ đã nhập, vì vậy, đại bàng đã rời khỏi tổ cảnh báo các đặc vụ .

84303245304270533808430637802537808430243687

Nhưng chờ đã! Một số trình tự T9 không rõ ràng (có thể là tên gọi là mật mã, có thể là tên của tên, chữ viết tắt, chữ viết tắt, chữ viết tắt, chữ viết tắt, chữ viết càng khó hiểu, nó càng bị nghi ngờ!), Vậy bạn sẽ làm gì? Bạn biết rằng bài kiểm tra đầu vào duy nhất mà M1S sử dụng là tóm tắt kiệt tác của Marcel Proust, Hồi ức về những điều đã qua trong 15 giây, vì vậy bạn muốn chọn từ tiếp theo sau bài trước theo phân phối tần số của nó trong toàn bộ đầu bếp-d ' œuvre của Proust!

Bạn có thể crack mã và có được những gì có thể là thông điệp ban đầu?

Nguyên tắc của T9

Bàn phím Nokia 3310 được sử dụng bởi các đại lý

Cơ chế tự động hoàn thành T9 có thể được mô tả như sau. Nó ánh xạ các ký tự chữ cái thành các số như trong hình trên.

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

Bộ giải mã T9 nhận được một chuỗi các chữ số và cố gắng đoán từ có thể được gõ bằng các phím nhấn đó. Nó có thể sử dụng bảng tần số tiêu chuẩn, nhưng chúng tôi sẽ tiến thêm một bước và dự đoán từ tiếp theo bằng chuỗi Markov!

Học mẫu

Các corpus là phiên bản này nặng nề tước “Remembrance of Things Past” Proust của ( s/-/ /g, s/['’]s //gs/[^a-zA-Z ]//g- begone bối rối sở hữu 's!) Ban đầu được công bố trên Đại học Adelaide trang web (các văn bản của tác phẩm này là trong phạm vi công cộng tại Úc).

Toàn bộ văn bản phải được phân tích thành một chuỗi, dưới dạng một câu dài, dưới dạng một vectơ dài của từ (bất kỳ ngôn ngữ nào thuận tiện hơn cho ngôn ngữ của bạn), loại bỏ các ngắt dòngchia thành các từ tại các khoảng trắng . (Tôi không cung cấp tệp một đoạn vì các công cụ github có thể bị nhăn mặt.)

Làm cách nào để đọc toàn bộ văn bản dưới dạng một chuỗi / câu? Một ví dụ trong R :

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

Bài tập

Đưa ra một chuỗi các chữ số dưới dạng một số, trả về một chuỗi văn bản có thể có thể được gõ bằng các phím T9 tương ứng bằng chuỗi xác suất để dự đoán từ X tiếp theo dựa trên văn bản đào tạo này được coi là một câu dài.

Nếu X là từ T9 đầu tiên của văn bản và có nhiều lần đoán, hãy chọn một từ ngẫu nhiên, nếu không thì chỉ chọn một từ có thể.

Đối với tất cả các từ T9 tiếp theo X (i) đứng trước một từ đã được giải mã w (i-1) :

  1. Nếu một T9 từ X có thể được chuyển đổi thành một từ x bình thường theo một cách duy nhất, hãy làm điều đó.
  2. Nếu có nhiều tùy chọn chuyển đổi có sẵn cho X , giả sử x1, x2, ... , hãy tìm từ đã đoán trước w .
    • Nếu w không bao giờ được theo sau bởi bất kỳ thứ gì ánh xạ tới X trong tác phẩm gốc của Proust, hãy chọn bất kỳ x1, x2, ... nào có thể ngẫu nhiên.
    • Nếu w X luôn tương ứng với w x1 trong bản gốc và không có xi xi đồng thời có thể được ánh xạ vào X , hãy chọn x1 .
    • Nếu w X có thể được chuyển đổi thành w x1 , w x2 , ... có thể tìm thấy trong kho văn bản, thì hãy đếm tất cả các xi xi có thể theo w và ánh xạ tới X trong kho và chọn xi với xác suất xi / (x1 + x2 + ...) .

Ví dụ 2a. Nếu thông điệp là 76630489, nơi 489có thể guyhoặc ivy(chúng xảy ra trong kho ít nhất một lần), 7663có thể được giải mã thành some(một từ đầu tiên rất có thể xảy ra). Nếu somekhông bao giờ được theo sau bởi bất cứ thứ gì ánh xạ 489vào kho văn bản, sau đó chọn guyhoặc ivyngẫu nhiên với xác suất 0,5.

Ví dụ 2b. Nếu thông điệp là 766302277437, nơi 2277437có thể barrierhoặc carrier, 7663có thể được giải mã là some. Nếu Proust luôn được sử dụng some carriervà không bao giờ some barrier, sau đó chọn some carrier.

Ví dụ 2c. Giả sử bạn muốn giải mã chuỗi 536307663. 5363đã được dự đoán là lend. 7663có thể là bất kỳ những: pond, roofsome. Bạn đếm số lần xuất hiện của từ sau lendtrong kho mẫu. Giả sử bạn nhận được một cái gì đó như thế này (chỉ để minh họa):

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

Vì vậy, nếu 7663được đi trước lend, có 7/(7+2+1)=70%xác suất 7663some20% pondvà 10% roof. Thuật toán của bạn sẽ tạo ra lend some70% trường hợp, lend pondtrong 20% ​​trường hợp, v.v.

Bạn có thể giả định một cách an toàn rằng các tác nhân chỉ sử dụng các chữ cái và dấu cách az (không có dấu chấm câu, không sở hữu 'svà không có số).

Bạn cũng có thể cho rằng các tác nhân của M1S không bao giờ sử dụng bất kỳ từ nào ngoài phạm vi của Remembrance of Things Past dòng (vốn là một từ vựng khổng lồ của 29.237 từ!).

Tính chất giả tưởng T9 đã được thực hiện trong thử thách này , vì vậy bạn có thể xem qua nó.

Nếu bạn cần bất kỳ sự giúp đỡ, chuỗi xác suất được hoành tráng thuần hóa trong này , đósau thử thách, nhưng bạn thậm chí không cần phải biết nguyên tắc của chuỗi như vậy: tất cả những gì được nêu trong công việc.

Các trường hợp thử nghiệm

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

Quy tắc:

  • Tiêu chuẩn áp dụng.
  • Bạn không biết tin nhắn gốc, tất cả những gì bạn nhận được là một chuỗi các chữ số và tệp proust.txt mà bạn chỉ cần tải trong bộ nhớ / không gian làm việc / bất cứ thứ gì. Không cần phải có bất cứ điều gì khép kín; giả định proust.txtluôn luôn có thể truy cập.
  • Thuật toán của bạn phải có khả năng tạo ra các kết quả đầu ra khác nhau với xác suất tương ứng nếu có nhiều hơn một tùy chọn giải mã có thể xảy ra theo kho văn bản (xem Ví dụ 2c).

Bạn cần giữ kín đáo nhất có thể, để mã ngắn nhất sẽ thắng!

PS Lợi ích rõ ràng của thuật toán xác suất này là thực tế là xác suất bạn sẽ có được một chuỗi gốc thực sự cho một chuỗi được giải mã mơ hồ có xu hướng một - chỉ cần chờ ...

PPS Xem thêm Dự đoán bằng cách khớp một phần .


Nhận xét của Peter Taylor từ hộp cát đã được tính đến. Đáng buồn thay, rất ít người trả lời trong tuần nó đã được đăng ở đó mặc dù có nhiều cập nhật, vì vậy mọi đề xuất đều được chào đón! BTW, đây là thử thách đầu tiên của tôi!
Andreï Kostyrka

Tôi nghi ngờ rằng một lý do lớn mà bạn không nhận được nhiều phản hồi là vì kiến ​​thức nâng cao cần thiết để hiểu vấn đề này. Nếu bạn đang muốn thách thức này để thu hút được một đám đông lớn hơn, tôi khuyên bạn nên bao gồm một số ví dụ trước đó cho thấy chuỗi Markov tại nơi làm việc :)
Nathan Merrill

@NathanMerrill OK, tôi đã thêm 3 liên kết đến các thử thách mẫu. Tuy nhiên, người dùng hoàn toàn không cần biết chuỗi Markov vì nhiệm vụ được mô tả trong thân câu hỏi theo thuật toán càng tốt: nếu X, hãy làm Y với xác suất thu được bằng cách tính Z trong mẫu học tập này. Tôi đã cố gắng làm cho nó tự túc như nó có được ...
Andreï Kostyrka

Ồ, tôi hiểu rồi. Nếu bạn không giải thích nó, tôi đã bỏ phiếu để đóng nó. Có vẻ như nó cần kiến ​​thức nâng cao :)
Nathan Merrill

1
Tôi thích thử thách này, nhưng tôi chưa có thời gian để ngồi xuống và tạo ra / giải pháp golf. Hy vọng điều đó sẽ xảy ra sớm.
Mego

Câu trả lời:


1

Giải pháp R, minh họa không cạnh tranh về những gì có thể được thực hiện

Đầu tiên, chúng tôi tải chuỗi từ vào bộ nhớ:

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

Thứ hai, chúng ta cần một chức năng mà T9-ifies bất kỳ văn bản nào:

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
}

Sau đó, chúng tôi T9-ify Proust:

p9 <- t9(proust)

Chuẩn bị cuối cùng: chúng tôi chia chuỗi đầu vào ở số không bằng cách sử dụng hàm mà chúng tôi gọi prep):

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

Và bây giờ tôi đề xuất một hàm lấy bất kỳ chuỗi số đầu vào nào, preps và giải mã từng từ một:

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
}

Và bây giờ những gì nó đang thực sự làm:

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" 

Ví dụ thứ hai:

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"

Xin đừng bình luận rằng điều này có thể được đánh golf. Có vẻ như ít người quan tâm đến thử thách này vì tính dài dòng khủng khiếp của tôi, vì vậy tôi đã đăng câu trả lời này để cho thấy một chương trình có thể trông như thế nào. Bạn không cần upvote / downvote câu trả lời này.


1

Python 3, 316 byte

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=' ')
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.