Keyfi Uzunluk Karma


16

Eğer bir var düşünün karma işlevi uzunluğu dizeleri alır ve getiri uzunluğu dizeleri ve onu olduğu güzel bir özelliği vardır dirençli çarpışma , yani iki farklı dizeleri bulmak zor ile aynı karma .'H2nnss''H(s)='H(s')

Artık , çarpışmalara karşı dirençli olurken , keyfi uzunluktaki dizeleri alıp uzunluktaki dizelerle eşleyen yeni bir karma işlev oluşturmak istiyorsunuz .'H'n

Şanslısınız, zaten 1979'da Merkle-Damgård inşaatı olarak bilinen ve tam olarak bunu başaran bir yöntem yayınlandı.

Bu zorluğun görevi bu algoritmayı uygulamak olacak, bu yüzden yaklaşımın daha basit olduğunu göstermesi gereken adım adım bir örnek geçmeden önce Merkle-Damgård yapısının resmi bir tanımına bakacağız. ilk başta görünebilir.

Bazı tamsayısı , yukarıda açıklandığı gibi bir karma işlevi ve keyfi uzunlukta bir giriş dizesi verildiğinde, yeni karma işlevi şunları yapar:n>0'Hs'H'

  • Set, uzunluğunu ve uzunluğundaki parçalar halinde bölün , gerekirse son parçayı sondaki sıfırlarla doldurun. Bu, olarak etiketlenmiş birçok .l=|s|ssnm=lnc1,c2,...,cm
  • Ekleme lider ve bir arka yığın ve , oluşan bir dizi olup sıfır ve olduğu ile şişirilir, ikili olarak gelen uzunluğu sıfır .c0cm+1c0ncm+1nn
  • Şimdi , önceki sonuca eklenen geçerli yinelemeli olarak uygulanır : , burada . (Aşağıdaki örneğe baktıktan sonra bu adım daha açık olabilir.)'Hcbenrben-1rben='H(rben-1cben)r0=c0
  • çıktısı nihai sonuç .'H'rm+1

Görev

Giriş olarak bir pozitif tamsayıyı alan bir program ya da işlev Yazın , bir karma işlev olarak kara kutu ve boş olmayan bir dizge ve geri dönüş aynı sonuç olarak aynı girdilere.n'Hs'H'

Bu , bu yüzden her dilde en kısa cevap kazanır.

Misal

Diyelim ki , bu yüzden verilen hash fonksiyonumuz 10 uzunluktaki dizeleri alır ve 5 uzunluktaki dizeleri döndürür.n=5'H

  • Bir giriş verilen , aşağıdaki parçaları almak: , , ve . , bir sıfır sonunda 5 uzunluğuna kadar doldurulması gerektiğini unutmayın .s="Programlama Bulmacaları"s1="Progr"s2="Ammin"s3="g Puz"s4="Zles0"s4
  • c0="00000" yalnızca beş oluşan bir dizedir ve ikili olarak beştir ( ) ve iki önde gelen sıfırla doldurulmuştur.c5="00101"101
  • Şimdi parçalar ile birleştirilmiştir :'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 .

için bazı seçimler 1'e bağlı olarak bu çıktının nasıl görüneceğine bir göz atalım :'H

  • Eğer , yani sadece her saniye karakteri döndürür elde ederiz: Yani böyle ise, çıkış olması gerekmektedir kara kutu fonksiyonu olarak verilir.'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
  • Eğer sadece kendi girişi ilk 5 karakter, çıkış döner olan . Benzer şekilde son 5 karakteri döndürürse, çıktı .HH"00000"H"00101"
  • Eğer çarpar bir karakterin giriş kodları ve döner bu numaranın ilk beş basamaklı, örneğin , daha sonra .HH("PPCG123456")="56613"H("Programming Puzzles")="91579"

1 Basitlik açısından, bu aslında çarpışmaya karşı dayanıklı değildir, ancak bu, gönderiminizi test etmek için önemli değildir.H



Söylemeliyim ki verilen örnek "OMG Puzzles!" etkili bir şekilde omgPzzles0. İyi seçilmiş örnek giriş!
LambdaBeta

H'nin giriş formatı üzerinde bir miktar esneklik olduğunu varsayabilir miyiz (örneğin, n uzunluğunda iki dizge veya yalnızca ilk 2n karakterini düşündüğü daha uzun bir dizge alır)?
Delfad0r

Örneğin "g P" arasında boşluk karakterleri geçerli çıktı mı?
guest271314

@ guest271314 Alan, ortaya çıkan karmanın bir parçasıysa, çıktısının alınması gerekir. Karma aslında "gP" ise, aralarında boşluk bırakamazsınız.
Laikoni

Yanıtlar:


7

Haskell , 91 90 86 bayt

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

Çevrimiçi deneyin!

açıklama

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

Dizeyi "00...0"( '0' n kez)a


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

Fonksiyon ?özyinelemeli uygulamayı uygular h: cşimdiye kadar elde ettiğimiz hash (uzunluk n ), zdizenin geri kalan kısmıdır. Boşsa z, sadece geri döneriz c, aksi takdirde (muhtemelen sıfırlarla doldurma ) ilk n karakterini zalır a, başa cve uygulanır h. Bu yeni karmayı verir ve sonra ?bu karmaya ve karakterinin geri kalan karakterlerine özyineli olarak çağırırız z.


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

İşlev !, aslında meydan okumayı çözen işlevdir . Bu alan n, hve sgirdi olarak (kapalı). Hesaplıyoruz a?sve tek yapmamız gereken nikili sayıya eklemek ve bir hkez daha uygulamak . n'ninmapM(:"1")a!!n ikili temsilini döndürür .n


1
letbir nöbetçi kullanmaktan daha kısadır where: Çevrimiçi deneyin!
Laikoni

2
Görünüşe mapM(\_->"01")aolabilir mapM(:"1")a.
xnor

7

R , 159156 bayt

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)

Çevrimiçi deneyin!

Yuck! R'deki zorluklarına cevap vermek hiç hoş değil, ama bu korkunç. Bu, "normal" R kodunun nasıl yazılmayacağına dair öğretici bir cevaptır ...

0 bayt maliyetiyle bir hatayı düzelttiği için nwellnhof'a teşekkürler !

Önceliği değiştirmek için operatörün takma adını değiştirdiği J.Doe sayesinde -4 bayt için iyi.

Aşağıdaki açıklama, kodun önceki sürümü içindir, ancak ilkeler aynı kalır.

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

Sanırım 0*(n-(?s)%%n)n eşit olarak bölünürse işe yaramaz. Ama 0*-((?s)%%-n)işe yaramalı.
nwellnhof

@nwellnhof ah, elbette, teşekkür ederim, düzeltildi.
Giuseppe

Küçük değişiklikler, 155 bayt
J.Doe

1
@ J.Doe güzel! Beri bir bayt kurtardı seqvardır 1onun kadar fromvarsayılan olarak argüman.
Giuseppe

3

C (gcc) , 251 bayt

#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);}

Çevrimiçi deneyin!

Bash çözümü kadar temiz değil ve son derece geliştirilebilir.

Fonksiyonudur falarak H, bu dizgenin karma ile dize giriş yerini alan bir fonksiyonu olarak nbilgi olarak, ve xgiriş dize ve çıkış tamponu.

Açıklama:

#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


Bence: 227 bayt (ceilingcat'in yorumundan çıkıyor)
Zacharý

3

Ruby , 78 bayt

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

Çevrimiçi deneyin!

Nasıl çalışır:

([?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-bayt

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"`)

Çevrimiçi deneyin!

Bu bir program / fonksiyon / kod / snippet olarak çalışır. H, karma işlemini gerçekleştirecek bir program veya işleve çözümlenebilir olmalıdır. N argüman. Örnek çağrı:

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

Açıklama:

Z=`printf %0*d $1`

Bu bir $1sıfır dizesi oluşturur . Bu, printf çağrısı yapılarak ve ekstra argüman genişliğine kadar dolgulu bir tam sayı yazdırmasını söyleyerek çalışır . Geçtiğimiz ekstra argüman $1, n'yi depolayan program / fonksiyon / betiğin argümanıdır.

R=$Z

Bu sadece karma dizeye hazırlanırken sıfır dizgimiz Z'yi sonuç dizgimiz olan R'ye kopyalar.

while IFS= read -rn$1 c; do

Bu $1, okunan karakterleri c'ye yükleyen her (n) karakterdeki girişin üzerinden geçer . Giriş sona ererse, c yalnızca çok kısa sona erer. Bu rseçenek, girişteki özel karakterlerin bash-yorumlanmamasını sağlar. Bu başlıktaki - rkesinlikle gerekli değildir, ancak işlevi girdiyle daha doğru bir şekilde eşleştirir.

R=$R$c$Z

Bu, doldurma için sıfırlarla (şimdilik şimdilik çok fazla sıfır) girişten R'ye okunan n karakterini birleştirir.

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

Burada, hash işlevine girdi olarak burada bir dize kullanılır. İçindekiler ${R::2*$1}biraz ezoterik bash parametresi ikamesidir: R, 0'dan başlayarak, sadece 2n karakterdir.

Burada döngü sona erer ve bitiririz:

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

Burada aynı format dize numarası numarayı 0 pad için kullanılır. bcçıktı tabanını (obase) 2 olarak ayarlayarak ikili sayıya dönüştürmek için kullanılır. Sonuç, çıktısı yakalanmayan ve böylece kullanıcıya gösterilen karma fonksiyona / programa aktarılır.


Neden "127-ε"? Neden sadece "127" değil?
Solomon Ucko

Bilmiyorum. rBayrağın gerekliliği konusunda çitin üzerindeydim . 1 baytın gerçekten önemli olmadığını düşündüm, ancak itilirse tıraş edebilirim.
LambdaBeta

For readkomutu?
Solomon Ucko

Çünkü onsuz `` girişte yok sayılmak yerine yorumlanacak, bu yüzden kaçmaları gerekecekti.
LambdaBeta

Belki bu konuda bir not ekleyin?
Solomon Ucko

2

Pyth , 24 bayt

Pyth, H'nin bir işlev adı için kullanılmasına izin vermediğinden, ybunun yerine kullanıyorum.

uy+GH+c.[E=`ZQQ.[ZQ.BQ*Z

Çevrimiçi deneyin! Örnek, H'nin "her ikinci karakter" versiyonudur.


2

Perl 6 , 79 68 bayt

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

Çevrimiçi deneyin!

açıklama

{
  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

Temiz , 143 bayt

import StdEnv
r=['0':r]
$n h s=foldl(\a b=h(a++b))(r%(1,n))([(s++r)%(i,i+n-1)\\i<-[0,n..length s]]++[['0'+toChar((n>>(n-p))rem 2)\\p<-[1..n]]])

Çevrimiçi deneyin!


1

Python 2 , 126113 bayt

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

Çevrimiçi deneyin!

-13 Triggernometry sayesinde .

Evet, bu bir iğrenç, neden sadece bir ipi parçalara ayırmak için yerleşik bir şey kullanamıyorum ...? :-(



@StevenH. Evet, özellikle de golf oynamaya odaklanıyorsanız. > _>
Outgolfer Erik

'0'*~-nyerine '0'*(len(s)%n)daha kısadır (ve aslında daha kısa girdiler için doğrudur).
nwellnhof

@nwellnhof Evet, ama kesinlikle aynı şey değil.
Outgolfer Erik

Belki yeterince açık değildim. Çözümünüz Programming Puzz(16 karakter) gibi dizeler için yanlış cevap veriyor . Değiştirme '0'*(len(s)%n)ile '0'*~-n7 bayt kaydeder ve düzeltmeler.
nwellnhof

1

Python 2 , 106102 bayt

Bir kez, fonksiyon lambda'yı geride bırakıyor. Jo King sayesinde basit sözdizimi düzenleme için -4 bayt.

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

Çevrimiçi deneyin!


Sonuç 'e011' değil 'Pe011' olmamalı mı?
Triggernometry

Olmalı. Sabit!
Steven H.

Yeni satırlar yerine noktalı virgül kullanın. -4 bayt
Jo King

Döngüler için de işe yaradığını fark etmedim, teşekkürler!
Steven H.

1

Japt , 27 bayt

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

Dene!

OvWgOxWW'H

sUnV'HW

Açıklama:

ò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

Tamam , 41 bayt

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

Çevrimiçi deneyin!

{                                       } /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
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.