Băm chiều dài tùy ý


16

Hãy xem xét bạn có hàm băm , lấy các chuỗi có độ dài và trả về các chuỗi có độ dài và có đặc tính tốt là nó có khả năng chống va chạm , nghĩa là khó tìm thấy hai chuỗi khác nhau với cùng băm .H2nnssH(s)=H(s)

Bây giờ bạn muốn xây dựng hàm băm mới , lấy các chuỗi có độ dài tùy ý và ánh xạ chúng thành các chuỗi có độ dài , trong khi vẫn có khả năng chống va chạm.H nn

May mắn cho bạn, đã có vào năm 1979, một phương pháp được biết đến với tên gọi xây dựng Damkård Merkle đã được xuất bản, đạt được chính xác điều này.

Nhiệm vụ của thử thách này sẽ là thực hiện thuật toán này, vì vậy trước tiên chúng ta sẽ xem xét một mô tả chính thức về cấu trúc MerkleTHER Damgård, trước khi đi qua một ví dụ từng bước sẽ cho thấy cách tiếp cận đơn giản hơn nó có thể xuất hiện lúc đầu

Cho một số nguyên , hàm băm như được mô tả ở trên và một chuỗi đầu vào có độ dài tùy ý, hàm băm mới thực hiện như sau:n>0HsH

  • Đặt, chiều dài của và chia thành các đoạn có độ dài , điền vào đoạn cuối cùng với các số 0 ở cuối nếu cần. Điều này mang lại nhiều khối được gắn nhãn .l=|s|ssnm = lm=lnc1,c2,,cm
  • Thêm một đoạn đầu và một đoạn cuối và , trong đó là một chuỗi bao gồm số không và là ở dạng nhị phân, được đệm bằng các số 0 dẫn đến độ dài .c0cm+1c0ncm+1nnn
  • Bây giờ lặp lại áp dụng cho đoạn hiện tại nối với kết quả trước đó : , trong đó . (Bước này có thể rõ ràng hơn sau khi xem ví dụ bên dưới.)Hciri1ri=H(ri1ci)r0=c0
  • Đầu ra của là kết quả cuối cùng .Hrm+1

Nhiệm vụ

Viết một chương trình hoặc chức năng mà mất như là đầu vào một số nguyên dương , một hàm băm như hộp đen và một chuỗi không rỗng và trả về kết quả tương tự như trên đầu vào tương tự.nHsH

Đây là , vì vậy câu trả lời ngắn nhất trong mỗi ngôn ngữ sẽ thắng.

Thí dụ

Giả sử , vì vậy hàm băm đã cho của chúng tôi lấy các chuỗi có độ dài 10 và trả về các chuỗi có độ dài 5.n=5H

  • Đưa ra đầu vào của , chúng tôi nhận được các đoạn sau: , , và . Lưu ý rằng cần được đệm đến chiều dài 5 với một số 0 ở cuối.s="Programming Puzzles"s1="Progr"s2="ammin"s3="g Puz"s4="zles0"s4
  • c0="00000" chỉ là một chuỗi gồm năm số không và là năm số nhị phân ( ), được đệm bằng hai số 0 đứng đầu.c5="00101"101
  • Bây giờ các khối được kết hợp với H :
    r0=c0="00000"
    r1=H(r0c1)=H("00000Progr")
    r2=H(r1c2)=H(H("00000Progr")"ammin") r3=H(r2c3)=H(H(H("00000Progr")"ammin")"g Puz")
    r4=H(r3c4)=H(H(H(H("00000Progr")"ammin")"g Puz")"zles0")
    r5=H(r4c5)=H(H(H(H(H("00000Progr")"ammin")"g Puz")"zles0")"00101")
  • r5 là đầu ra của chúng tôi.

Chúng ta hãy xem đầu ra này trông như thế nào tùy thuộc vào một số lựa chọn 1 cho :H

  • Nếu , tức là chỉ trả về mỗi ký tự thứ hai, chúng tôi nhận được: Vì vậy, cần phải là đầu ra nếu một được đưa ra dưới dạng hàm hộp đen.H("0123456789")="13579"H
    r1=H("00000Progr")="00Por"
    r2=H("00Porammin")="0oamn"
    r3=H("0oamng Puz")="omgPz"
    r4=H("omgPzzles0")="mPze0"
    r5=H("mPze000101")="Pe011""Pe011"
    "Pe011"H
  • Nếu chỉ trả về 5 ký tự đầu tiên của đầu vào, thì đầu ra của là . Tương tự nếu trả về 5 ký tự cuối cùng, đầu ra là .HH"00000"H"00101"
  • Nếu nhân mã số ký tự của đầu vào của nó và trả về năm chữ số đầu tiên của số này, ví dụ , sau đó .HH("PPCG123456")="56613"H("Programming Puzzles")="91579"

1 Để đơn giản, những thực sự không có khả năng chống va chạm, mặc dù điều này không quan trọng để kiểm tra trình của bạn.H



Tôi phải nói thật vui khi ví dụ đưa ra có hàm băm 'đầy đủ' cuối cùng là "Câu đố OMG!" một cách hiệu quả omgPzzles0. Ví dụ đầu vào cũng được chọn!
LambdaBeta

Chúng ta có thể giả sử một số tính linh hoạt trên định dạng đầu vào cho H (ví dụ: phải mất hai chuỗi có độ dài n hoặc chuỗi dài hơn mà nó chỉ xem xét 2 ký tự đầu tiên)?
Delfad0r

Các ký tự không gian, ví dụ, giữa "g P" đầu ra hợp lệ?
khách271314

@ guest271314 Nếu không gian là một phần của hàm băm kết quả, nó cần được xuất ra. Nếu hàm băm thực sự là "gP", bạn không thể xuất ra một khoảng trắng ở giữa.
Laikoni

Câu trả lời:


7

Haskell , 91 90 86 byte

n!h|let a='0'<$[1..n];c?""=c;c?z=h(c++take n(z++a))?drop n z=h.(++mapM(:"1")a!!n).(a?)

Hãy thử trực tuyến!

Giải trình

a='0'<$[1..n]

Chỉ cần gán chuỗi "00...0"( '0' n lần) choa


c?""=c
c?z=h(c++take n(z++a))?drop n z

Hàm ?thực hiện ứng dụng đệ quy của h: clà hàm băm mà chúng ta đã thu được cho đến nay (độ dài n ), zlà phần còn lại của chuỗi. Nếu ztrống thì chúng ta chỉ cần trả về c, nếu không, chúng ta lấy n ký tự đầu tiên của z(có thể là đệm với số không từ a), thêm vào cvà áp dụng h. Điều này mang lại hàm băm mới, và sau đó chúng ta gọi ?đệ quy trên hàm băm này và các ký tự còn lại của z.


n!h=h.(++mapM(:"1")a!!n).(a?)

Các chức năng !là một trong những thực sự giải quyết thách thức. Nó nhận n, hs(ngầm) làm đầu vào. Chúng tôi tính toán a?s, và tất cả những gì chúng tôi phải làm là nối thêm nvào nhị phân và áp dụng hmột lần nữa. mapM(:"1")a!!ntrả về biểu diễn nhị phân của n .


1
lettrong một bảo vệ ngắn hơn so với sử dụng where: Hãy thử trực tuyến!
Laikoni

2
Có vẻ như mapM(\_->"01")acó thể mapM(:"1")a.
xnor

7

R , 159 154 byte

function(n,H,s,`?`=paste0,`*`=strrep,`/`=Reduce,`+`=nchar,S=0*n?s?0*-(+s%%-n)?"?"/n%/%2^(n:1-1)%%2)(function(x,y)H(x?y))/substring(S,s<-seq(,+S,n),s--n-1)

Hãy thử trực tuyến!

Kinh quá! Trả lời thử thách trong R không bao giờ đẹp, nhưng điều này thật kinh khủng. Đây là câu trả lời mang tính hướng dẫn về cách không viết mã R "bình thường" ...

Cảm ơn nwellnhof đã sửa lỗi, với chi phí 0 byte!

Cảm ơn J.Doe đã hoán đổi toán tử bí danh để thay đổi mức ưu tiên, tốt cho -4 byte.

Giải thích dưới đây dành cho phiên bản trước của mã, nhưng các nguyên tắc vẫn giữ nguyên.

function(n,H,s,               # harmless-looking function arguments with horrible default arguments 
                              # to prevent the use of {} and save two bytes
                              # then come the default arguments,
                              # replacing operators as aliases for commonly used functions:
 `+`=paste0,                  # paste0 with binary +
 `*`=strrep,                  # strrep for binary *
 `/`=Reduce,                  # Reduce with binary /
 `?`=nchar,                   # nchar with unary ?
 S=                           # final default argument S, the padded string:
  0*n+                        # rep 0 n times
  s+                          # the original string
  0*-((?s)%%-n)+              # 0 padding as a multiple of n
  "+"/n%/%2^(n:1-1)%%2)       # n as an n-bit number
                              # finally, the function body:
 (function(x,y)H(x+y)) /      # Reduce/Fold (/) by H operating on x + y
  substring(S,seq(1,?S,n),seq(n,?S,n))  # operating on the n-length substrings of S

Tôi nghĩ rằng 0*(n-(?s)%%n)không làm việc nếu n chia đều. Nhưng 0*-((?s)%%-n)nên làm việc.
nwellnhof

@nwellnhof ah, tất nhiên, cảm ơn, đã sửa.
Giuseppe

Thay đổi nhỏ, 155 byte
J.Doe

1
@ J.Doe đẹp quá! Tôi lưu byte khác kể từ khi seq1như nó fromlập luận theo mặc định.
Giuseppe

3

C (gcc) , 251 byte

#define P sprintf(R,
b(_){_=_>1?10*b(_/2)+_%2:_;}f(H,n,x)void(*H)(char*);char*x;{char R[2*n+1],c[n+1],*X=x;P"%0*d",n,0);while(strlen(x)>n){strncpy(c,x,n);x+=n;strcat(R,c);H(R);}P"%s%s%0*d",R,x,n-strlen(x),0);H(R);P"%s%0*d",R,n,b(n));H(R);strcpy(X,R);}

Hãy thử trực tuyến!

Không sạch như giải pháp bash, và rất ngẫu hứng.

Hàm đang fdùng Hnhư một hàm thay thế đầu vào chuỗi của nó bằng hàm băm của chuỗi đó, nnhư trong mô tả, và xchuỗi đầu vào và bộ đệm đầu ra.

Sự miêu tả:

#define P sprintf(R,     // Replace P with sprintf(R, leading to unbalanced parenthesis
                         // This is replaced and expanded for the rest of the description
b(_){                    // Define b(x). It will return the integer binary expansion of _
                         // e.g. 5 -> 101 (still as integer)
  _=_>1?                 // If _ is greater than 1
    10*b(_/2)+_%2        // return 10*binary expansion of _/2 + last binary digit
    :_;}                 // otherwise just _
f(H,n,x)                 // Define f(H,n,x)
  void(*H)(char*);       // H is a function taking a string
  char*x; {              // x is a string
  char R[2*n+1],c[n+1],  // Declare R as a 2n-length string and c as a n-length string
  *X=x;                  // save x so we can overwrite it later
  sprintf(R,"%0*d",n,0); // print 'n' 0's into R
  while(strlen(x)>n){    // while x has at least n characters
    strncpy(c,x,n);x+=n; // 'move' the first n characters of x into c
    strcat(R,c);         // concatenate c and R
    H(R);}               // Hash R
  sprintf(R,"%s%s%0*d"   // set R to two strings concatenated followed by some zeroes
    R,x,                 // the two strings being R and (what's left of) x
    n-strlen(x),0);      // and n-len(x) zeroes
  H(R);                  // Hash R
  sprintf(R,"%s%*d",R,n, // append to R the decimal number, 0 padded to width n
    b(n));               // The binary expansion of n as a decimal number
  H(R);strcpy(X,R);}     // Hash R and copy it into where x used to be


Tôi nghĩ: 227 byte (tắt bình luận của
Barecat

3

Ruby , 78 byte

->n,s,g{(([?0*n]*2*s).chop.scan(/.{#{n}}/)+["%0#{n}b"%n]).reduce{|s,x|g[s+x]}}

Hãy thử trực tuyến!

Làm thế nào nó hoạt động:

([?0*n]*2*s).chop    # Padding: add leading and trailing 
                     # zeros, then remove the last one
.scan(/.{#{n}}/)     # Split the string into chunks
                     # of length n
+["%0#{n}b"%n]       # Add the trailing block
.reduce{|s,x|g[s+x]} # Apply the hashing function
                     # repeatedly


2

Bash , 127-byte

Z=`printf %0*d $1` R=$Z
while IFS= read -rn$1 c;do R=$R$c$Z;R=`H<<<${R::2*$1}`;done
H< <(printf $R%0*d $1 `bc <<<"obase=2;$1"`)

Hãy thử trực tuyến!

Điều này hoạt động như một chương trình / chức năng / kịch bản / đoạn. H phải được phân giải theo chương trình hoặc chức năng sẽ thực hiện băm. N là đối số. Cuộc gọi ví dụ:

$ H() {
>   sed 's/.\(.\)/\1/g'
> }
$ ./wherever_you_put_the_script.sh 5 <<< "Programming Puzzles"  # if you add a shebang
Pe011

Sự miêu tả:

Z=`printf %0*d $1`

Điều này tạo ra một chuỗi $1số không. Điều này hoạt động bằng cách gọi printf và yêu cầu nó in một số nguyên được thêm vào chiều rộng đối số thêm . Đối số bổ sung mà chúng ta chuyển là $1, đối số cho chương trình / hàm / tập lệnh lưu n.

R=$Z

Điều này chỉ sao chép Z, chuỗi số 0 của chúng tôi, thành R, chuỗi kết quả của chúng tôi, để chuẩn bị cho vòng băm.

while IFS= read -rn$1 c; do

Vòng lặp này trên đầu vào mỗi $1(n) ký tự tải các ký tự đọc vào c. Nếu đầu vào kết thúc thì c chỉ kết thúc quá ngắn. Các rĐảm bảo lựa chọn mà bất kỳ ký tự đặc biệt trong đầu vào không được bash-giải thích. Đây là tiêu đề - rkhông thực sự cần thiết, nhưng làm cho chức năng khớp chính xác hơn với đầu vào.

R=$R$c$Z

Điều này nối các ký tự n được đọc từ đầu vào đến R cùng với các số 0 để đệm (hiện tại có quá nhiều số 0).

R=`H<<<${R::2*$1}`;done

Điều này sử dụng một chuỗi ở đây làm đầu vào cho hàm băm. Nội dung ${R::2*$1}là một thay thế tham số bash bí truyền có nội dung: R, bắt đầu từ 0, chỉ có 2n ký tự.

Ở đây vòng lặp kết thúc và chúng tôi kết thúc với:

H< <(printf $R%0*d $1 `bc <<<"obase=2;$1"`)

Ở đây, thủ thuật chuỗi định dạng tương tự được sử dụng để 0 pad số. bcđược sử dụng để chuyển đổi nó thành nhị phân bằng cách đặt cơ sở đầu ra (obase) thành 2. Kết quả được chuyển đến hàm / chương trình băm có đầu ra không được ghi lại và do đó được hiển thị cho người dùng.


Tại sao "127-ε"? Tại sao không chỉ là "127"?
Solomon Ucko

Tôi không biết. Tôi đã ở trên hàng rào về sự cần thiết của rcờ. Tôi đã tìm ra 1 byte không thực sự quan trọng, nhưng nếu bị đẩy tôi có thể cạo nó.
LambdaBeta

Cho readlệnh?
Solomon Ucko

Bởi vì không có nó, một `` trong đầu vào sẽ được diễn giải thay vì bỏ qua, vì vậy chúng phải được thoát.
LambdaBeta

Có thể thêm một lưu ý về điều đó?
Solomon Ucko


2

Perl 6 , 79 68 byte

{reduce &^h o&[~],comb 0 x$^n~$^s~$n.fmt("%.{$n-$s.comb%-$n}b"): $n}

Hãy thử trực tuyến!

Giải trình

{
  reduce         # Reduce with
    &^h o&[~],   # composition of string concat and hash function
    comb         # Split string
      0 x$^n     # Zero repeated n times
      ~$^s       # Append input string s
      ~$n.fmt("  # Append n formatted
        %.       # with leading zeroes,
        {$n             # field width n for final chunk
         -$s.comb%-$n}  # -(len(s)%-n) for padding,
        b")      # as binary number
      :          # Method call with colon syntax
      $n         # Split into substrings of length n
}


1

Python 2 , 126 113 byte

lambda n,H,s:reduce(lambda x,y:H(x+y),re.findall('.'*n,'0'*n+s+'0'*(n-len(s)%n))+[bin(n)[2:].zfill(n)])
import re

Hãy thử trực tuyến!

-13 nhờ vào Triggernometry .

Vâng, đây là một sự gớm ghiếc, tại sao tôi không thể sử dụng một tích hợp để tách một chuỗi thành nhiều phần ...? :-(


codegolf.stackexchange.com/a/173952/55696 Một whilevòng lặp là nội dung tốt nhất mà tôi có thể hy vọng. 104 byte
Steven H.

@StevenH. Vâng, đặc biệt nếu bạn thực sự tập trung vào việc chơi golf. > _>
Erik the Outgolfer

'0'*~-nthay vì '0'*(len(s)%n)ngắn hơn (và thực sự chính xác cho đầu vào ngắn hơn).
nwellnhof

@nwellnhof Vâng, nhưng nó chắc chắn không phải là điều tương tự.
Erik the Outgolfer 14/10/18

Có lẽ tôi đã không đủ rõ ràng. Giải pháp của bạn đưa ra câu trả lời sai cho các chuỗi như Programming Puzz(16 ký tự). Thay thế '0'*(len(s)%n)bằng các '0'*~-nsửa lỗi đó và tiết kiệm 7 byte.
nwellnhof

1

Python 2 , 106 102 byte

Lần đầu tiên, chức năng vượt qua lambda. -4 byte cho thao tác cú pháp đơn giản, nhờ Jo King.

def f(n,H,s):
 x='0'*n;s+='0'*(n-len(s)%n)+bin(n)[2:].zfill(n)
 while s:x=H(x+s[:n]);s=s[n:]
 return x

Hãy thử trực tuyến!


Không phải kết quả là 'Pe011', không phải 'e011'?
Triggernometry 16/10/18

Điều đó nên. Đã sửa!
Steven H.

Sử dụng dấu chấm phẩy thay vì dòng mới. -4 byte
Jo King

Tôi đã không nhận ra rằng làm việc trong khi vòng lặp là tốt, cảm ơn!
Steven H.

1

Japt , 27 byte

òV ú'0 pV¤ùTV)rÈ+Y gOvW}VçT

Thử nó!

OvWgOxWWH

sUnVHW

Giải trình:

òV                             Split U into segments of length V
   ú'0                         Right-pad the short segment with "0" to the same length as the others
       p     )                 Add an extra element:
        V¤                       V as a base-2 string
          ùTV                    Left-pad with "0" until it is V digits long
              r                Reduce...
                        VçT          ...Starting with "0" repeated V times...
               È       }                                                  ...By applying:
                +Y               Combine with the previous result
                   gOvW          And run W as Japt code



0

oK , 41 byte

{(x#48)(y@,)/(0N,x)#z,,/$((x+x!-#z)#2)\x}

Hãy thử trực tuyến!

{                                       } /x is n, y is H, z is s.
                          (x+x!-#z)       /number of padding 0's needed + x
                         (         #2)\x  /binary(x) with this length
                      ,/$                 /to string
                    z,                    /append to z
             (0N,x)#                      /split into groups of length x
       (y@,)/                             /foldl of y(concat(left, right))...
 (x#48)                                   /...with "0"*x as the first left string
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.