Marcel Proust 및 Markov 해독 보안 서비스의 T9 텍스트


11

이 문제는 더 이상이 될 수있는 것처럼 Pythonesque을 정신 ... 마르코프 체인 또는 암호화 기술에 대한 사전 지식은 필요하지 않습니다.

영국 보안 서비스 M1S로부터 중요한 정보를 얻어야하는 스파이입니다. M1S의 에이전트는 Wi-Fi 신호를 가로 챌 수 있고 Android / iOS 보안 취약점을 악용 할 수 있다는 점을 잘 알고 있으므로 모두 Nokia 3310을 사용하여 T9 자동 완성 기능을 사용하여 입력 한 텍스트 정보를 전송하고 있습니다.

당신은 이제 당신이 그렇게 "가 입력 한 문자에 해당하는 숫자의 순서를 받기 이전에 정보 기관에 전달 될 수있는 전화를 해킹했고 자신의 영광스러운 플라스틱 키보드 아래에 키로거를 설치 한 독수리는 에이전트가 둥지 경고 떠났다 가된다"를

84303245304270533808430637802537808430243687

하지만 기다려! 일부 T9 시퀀스는 모호합니다 ( "6263"은 "이름", "갈기"또는 "oboe"일 수 있습니다. 더 모호할수록 더 의심됩니다!). 어떻게해야합니까? M1S가 사용하는 유일한 입시 시험은 15 초 만에 Marcel Proust의 걸작“과거의 추억”을 요약하는 것입니다. – 자유로운 자외선!

코드를 해독하고 원래 메시지가 될 수있는 것을 얻을 수 있습니까?

T9의 원리

에이전트가 사용하는 Nokia 3310 키보드

T9 자동 완성 메커니즘은 다음과 같이 설명 할 수 있습니다. 위의 그림과 같이 알파벳 문자를 숫자로 매핑합니다.

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

T9 암호 해독기는 일련의 숫자를 수신하고 해당 키 누름으로 입력 할 수있는 단어를 추측하려고합니다. 표준 빈도 표를 사용할 수도 있지만 Markov 체인을 사용하여 한 단계 더 나아가 다음 단어를 예측하고 있습니다!

학습 샘플

코퍼스는 프루스트의 "것 과거의 기억"이 많이 제거 버전 ( s/-/ /g, s/['’]s //gs/[^a-zA-Z ]//g- 물러 가라 혼란 소유 's!) 원래에 게시 애들레이드 대학교의 웹 사이트 (작품의 텍스트가 호주에서 공개 도메인에있다).

전체 텍스트는 하나 개의 긴 단어의 벡터로, 하나 개의 긴 문장으로, 하나의 문자열로 분석해야한다 (해당 언어에 대한 더 편리 중), 줄 바꿈을 박탈 하고, 공간의 단어로 분할 . (단일 단락 파일은 github 도구에 의해 찌그러 질 수 있으므로 제공하지 않습니다.)

전체 텍스트를 하나의 문자열 / 문장으로 읽는 방법은 무엇입니까? 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

직무

숫자로 된 일련의 숫자가 주어지면 확률 체인을 사용하여 해당 T9 키를 사용하여 입력 할 수있는 가능한 문자열을 반환하여 긴 문장으로 취급되는 이 훈련 텍스트를 기반으로 다음 단어 X 를 예측하십시오 .

경우 X는 텍스트의 첫 번째 T9 단어이고, 거기에 여러 추측이 있습니다 무작위로 하나를 선택, 그렇지 않으면 가능한 하나를 선택하십시오.

이후의 모든 T9 단어 X (i) 앞에는 이미 해독 된 단어 w (i-1) 이옵니다 .

  1. T9- 워드 X 를 고유 한 방식으로 일반 워드 x 로 변환 할 수 있다면 그렇게하십시오.
  2. x에 사용할 수있는 여러 변환 옵션 ( 예 : x1, x2, ... )이있는 경우 앞의 추측 된 단어 w를 찾으십시오 .
    • w 다음에 Proust의 원래 작업에서 X 에 매핑되는 항목이없는 경우 가능한 x1, x2 등 을 임의로 선택하십시오.
    • 경우 X 승 항상에 해당하는 1 개 w 원본과 더 동시 없습니다 XI이 의 그에 매핑 할 수 있습니다 X , 선택 1 개를 .
    • 경우 X 승 으로 변환 할 수 있습니다 X1 승 , 2 배 승 ... 그 코퍼스에서 찾을 수 있습니다, 모든 가능한 계산 XI 것은 '그 후속이야 및 매핑 X 코퍼스와 선택 XI을 확률로 XI / (1 개 + x2 + ...) .

실시 예 2a. 이 메시지가 76630489어디에 489있을 수 guy있거나 ivy(적어도 한 번 코퍼스에서 발생하는 경우) (매우 가능성이 높은 첫 단어) 7663로 해독 될 수 있습니다 some. 만약 some에 매핑 아무것도 뒤에되지 않습니다 489코퍼스에서 다음 선택 guy또는 ivy확률 0.5 무작위로.

실시 예 2b. 메시지 인 경우 766302277437, 어디 2277437barrier또는 carrier, 7663로 해독 할 수 있습니다 some. Proust가 항상 사용 some carrier하고 절대 사용 하지 않으면 some barrier을 선택하십시오 some carrier.

실시 예 2c. 시퀀스를 해독한다고 가정합니다 536307663. 5363로 예측되었습니다 lend. 7663이들의 수 : pond, roofsome. lend샘플 모음에서 다음 단어의 발생 횟수를 계산합니다 . 다음과 같은 것을 얻는다고 가정하십시오 (설명하기 위해).

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

그렇다면 7663앞에는 lend,이 7/(7+2+1)=70%확률 7663을 의미 some20 %, pond10 % roof. 알고리즘은 lend some70 % 경우, lend pond20 % 경우 등에서 생성해야합니다 .

상담원은 az 문자와 공백 만 사용한다고 가정 할 수 있습니다 (마침표, 소유 's및 숫자 없음).

또한 M1S 요원이 “과거의 기억 (Remembrance of Things Past)” (29,237 단어의 엄청나게 많은 단어) 의 범위를 벗어나는 단어를 절대 사용하지 않는다고 가정 할 수 있습니다 .

이 과제 에서 T9 기능이 구현 되었으므로 살펴볼 수도 있습니다.

당신이 어떤 도움이 필요하면, 확률 체인 영광에 지배 된 , 다음 도전,하지만 당신은 심지어 체인의 원리를 알 필요가 없습니다 : 모든 것이 작업에 명시되어있다.

테스트 사례

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

규칙 :

  • 표준 허점이 적용됩니다.
  • 당신은 원래 메시지를 모르고, 당신이 얻는 것은 일련의 숫자와 메모리 / 작업 공간 / 무엇이든로드 해야하는 proust.txt 파일 입니다. 독립적 인 것을 가질 필요는 없습니다. proust.txt항상 액세스 할 수 있다고 가정합니다 .
  • 모음에 따라 둘 이상의 암호 해독 옵션을 사용할 수있는 경우 알고리즘에서 각 확률로 다른 출력을 생성 할 수 있어야합니다 (예 2c 참조).

가능한 한 신중하게 유지해야하므로 가장 짧은 코드가 승리합니다!

PS 이 확률 적 알고리즘의 명백한 이점은 모호한 해독 된 문자열에 대해 실제 원본 문자열을 얻을 확률이 1 인 경향이 있다는 사실입니다.

PPS 부분 일치 예측을 참조하십시오 .


샌드 박스 의 Peter Taylor의 말이 고려되었습니다. 안타깝게도 여러 업데이트에도 불구하고 게시 된 주에 응답 한 사람이 거의 없었으므로 제안을 환영합니다! BTW, 이것은 나의 첫 번째 도전입니다!
Andreï Kostyrka

많은 답변을 얻지 못한 큰 이유는이 문제를 이해하는 데 필요한 고급 지식 때문이라고 생각합니다. 당신이하면 되는 큰 군중에 호소에이 문제를 원하는, 내가 :) 직장에서 마르코프 체인을 보여 일부 이전 예제를 포함하여 권하고 싶습니다
나단 메릴

@NathanMerrill OK, 저는 샘플 챌린지에 3 개의 링크를 추가했습니다. 그러나 과제는 질문 본문에 가능한 알고리즘 적으로 설명되어 있기 때문에 사용자는 Markov 체인을 전혀 알 필요가 없습니다. X 인 경우이 학습 샘플에서 Z를 계산하여 얻은 확률로 Y를 수행하십시오. 나는 그것을 얻을만큼 자급 자족하려고 노력했습니다 ...
Andreï Kostyrka

아 나 이해 했어. 당신이 그것을 설명하지 않았다면, 나는 그것을 폐쇄하기로 투표했을 것입니다. 그냥 보이는 이 :) 고급 지식을 필요로 같은
나단 메릴

1
나는이 도전을 좋아하지만 아직 솔루션을 만들거나 골퍼 할 시간이 없었습니다. 바라건대 곧 일어날 것입니다.
Mego

답변:


1

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

둘째, 텍스트를 T9로 수정하는 함수가 필요합니다.

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
}

그런 다음 T9-ify Proust :

p9 <- t9(proust)

최종 준비 : 우리는 호출하는 함수를 사용하여 입력 문자열을 0으로 나눕니다 prep.

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

이제 입력 문자열을 가져 와서 prep단어를 하나씩 해독 하는 함수를 제안합니다 .

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
}

그리고 지금 실제로하고있는 일 :

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" 

두 번째 예 :

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"

이 골프를 할 수 있다고 언급하지 마십시오. 내 끔찍한 사실로 인해이 도전에 관심을 가진 사람은 거의없는 것 같습니다. 그래서 가능한 대답을 보여주기 위해이 답변을 게시했습니다. 이 답변을 찬성 / 감속하지 않아도됩니다.


1

파이썬 3, 316 바이트

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=' ')
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.