Bir kelimeyi eşit puanlara sahip parçalara ayırın


9

A = 1, B = 2 ... Z = 26 olduğu ve bir kelimenin değeri bu harf değerlerinin toplamı olduğu varsayıldığında, bazı kelimeleri eşit değerlere sahip olacak şekilde iki parçaya bölmek mümkündür.

Örneğin, "wordplit" şu şekilde iki parçaya ayrılabilir: ordsl wpit, çünkü o + r + d + s + l = w + p + i + t.

Bu, bilgisayar öğretmenim tarafından bize verilen bir meydan okumaydı - görünüşe göre eski bir Lionhead Studios meydan okuması. Python'da çözdüm ve kısa sürede cevabımı göndereceğim.

Zor Görev: Eşit puanlara sahip tüm olası bölünmeleri listeleyebilen en kısa program . Her harf grubu için yalnızca bir tane listelemesi gerektiğini unutmayın - ordsl wpit, örneğin rdosl wtip ile aynıdır. Bunları kelimeye geldikleri sırayla listelemek daha kolaydır.

Bonus:

  • Her iki sözcüğün geçerli İngilizce sözcükler (veya harflerin bazı permütasyonları) olduğu çiftleri vurgularsanız, bir tür sözcük listesi kullanarak. (Bu, her bir veya başka bir yöntemin yanına yıldız işareti koyarak yapılabilir, ancak açıklığa kavuşturun.)
  • Yinelenenleri kaldırma seçeneği ekleniyor (bu varsayılan olmamalıdır.)
  • İkiden fazla bölmeyi destekler, örneğin, üç, dört veya hatta n yollu bölmeler.

Program karışık kutu girişini desteklemeli mi? Ve eğer öyleyse çıktı için durumu atabilir mi?
Nemo157

@ Nemo157 Örneği yok sayabilir ve çıktıda saklamak zorunda değildir.
Thomas O

Çıktının talep edilen kısmı bir insan için açık olduğu sürece, program ekstra şeyler çıkarabilir mi?
JB

@JB Evet olabilir.
Thomas O

tamam, o zaman Perl'i geliştireceğim;) Teşekkürler
JB

Yanıtlar:


4

Perl, 115 118 123

@_=~/./g;for$i(1..1<<@_){$l=$
r;$i&1<<$_?$l:$r+=64-ord$_[$_
]for 0..$#_;$l-$r||$i&1<<$_&&
print$_[$_]for 0..$#_;say""}

İle çalıştırın perl -nE '<code goes here>'. Bu 'n' kod boyutunda sayılır.

Respaced:

@_ = /./g;
for $i (1 .. 1<<@_) {
  $l = $r;
  $i & 1<<$_ ? $l : $r -= 64 - ord $_[$_] for 0 .. $#_;

  $l - $r      ||
  $i & 1<<$_   &&
  print $_[$_]
    for 0 .. $#_;

  say ""
}

Yorum ve değişken adlarıyla:

# split a line of input by character
@chars = /./g;

# generate all binary masks of same length
for $mask (1 .. 1<<@_) {

  # start at "zero"
  $left_sum = $right_sum;

  # depending on mask, choose left or right count
  # 1 -> char goes left; 0 -> char goes right
  $mask & 1<<$_ ? $left_sum : $right_sum
    -= 64 - ord $chars[$_]   # add letter value
      for 0 .. $#chars;      # for all bits in mask

  # if left = right
  $left_sum - $right_sum ||

  # if character was counted left (mask[i] = 1)
  $mask & 1<<$_          &&

  # print it
  print $chars[$_]

  # ...iterating on all bits in mask
    for 0 .. $#chars;

  # newline
  say ""
}

Kullanılan hilelerden bazıları:

  • 1..1<<@_ile aynı bit aralığını kapsar 0..(1<<@_)-1, ancak daha kısadır. (aralık sınırları birden çok kez dahil olmak üzere, sorunu daha uzağa düşünmenin yine de yanlış bir çıktıya yol açmayacağını unutmayın)
  • $ left_range ve $ right_range gerçek "0" sayısal sıfıra sıfırlanmaz: en sonunda bunları biriktirip karşılaştırdığımız için, tek ihtiyacımız aynı değerle başlamaktır.
  • 64-ord$_[$_]eklemek yerine çıkartmak ord$_[$_]-64görünmez bir karakter kazanır: bir sınırlayıcı ile bittiği için, alanı forgereksiz kılar .
  • Perl Eğer üçlü koşullu operatör tarafından belirlenen bir değişkene atamanızı sağlar: cond ? var1 : var2 = new_value.
  • uygun şartlar yerine zincirleme &&ve boolean ifadeler ||kullanılır.
  • $l-$r daha kısa $l!=$r
  • dengelenmeyen bölünmelerde bile yeni satır çıktısı alır. Boş çizgiler kurallara uygun! Diye sordum!

Hat gürültüsü konuşmayanlar için açıklamak ister misiniz? Benimkine benzer bir ikili maske yaklaşımı kullandığınıza benziyor ve 64'ün '@' = 'A' - 1 anlamına geldiğini görüyorum ve bundan sonra neredeyse kayboldum.
dmckee --- eski moderatör yavru kedi

Bu düzenleme daha iyi mi?
JB

Güzel. Ben sol veya sağ toplamı her sayıyı eklemek yararlanmak düşünmek gerekir. Açık olmalıydı, ama özledim.
dmckee --- eski moderatör yavru kedi

3

J (109)

~.(/:{[)@:{&a.@(96&+)&.>>(>@(=/@:(+/"1&>)&.>)#[),}.@(split~&.>i.@#@>)@<@(96-~a.&i.)"1([{~(i.@!A.i.)@#)1!:1[1

Çıktı wordsplit:

┌─────┬─────┐
│lorw │dipst│
├─────┼─────┤
İltdiltw│oprs │
├─────┼─────┤
│iptw │dlors│
├─────┼─────┤
│dlors│iptw │
├─────┼─────┤
│oprs │diltw│
├─────┼─────┤
│dipst│lorw │
└─────┴─────┘

Açıklama:

  • 1!:1[1: stdin'den bir satır oku
  • ([{~(i.@!A.i.)@#): tüm permütasyonları al
  • "1: her permütasyon için:
  • (96-~a.&i.): mektup puanı al
  • }.@(split~&.>i.@#@>)@<: puanların her permütasyonunu, ilk sayıdan önce ve son sayıdan sonra hariç olmak üzere, olası her alanda bölün
  • >(>@(=/@:(+/"1&>)&.>)#[): hangi permütasyonların eşleşen yarıya sahip olduğunu görün ve bunları seçin
  • {&a.@(96&+)&.>: puanları tekrar harflere çevirin
  • ~.(/:{[): önemsiz varyasyonları kaldırın (ör. ordsl wpitve ordsl wpti)

Yanıtlarınızdan bazıları kopya.
DavidC

@DavidCarraher: Ya körüm ya da bu değil ya da son yanıtlarım. Asla başkalarının cevaplarını bilerek kopyalamam, tabii ki haklı olabilirsin, bazen sarhoşken buraya gönderdim ve bir sürü downvotes alana kadar hatırlamıyorum ve hatta olmayan bir şey gönderdiğim ortaya çıktı düzeltmek için kapatın. Beni yanlış davrandığını gördüysen, belki de yanlış davrandığım yorumu bırak, o zaman rahatsız edici cevapları sileceğim; ya da belki de aşağı oylar, çünkü aşağı oylar bunun içindir.
marinus

Hafif bir amaç yoktu. Basitçe, örneğin, ilk cevabınız {"lorw", "dipst"}, nihai cevabınızın bir kopyası, {"dipst", "lorw"}. Sadece kelimelerin sırası farklıdır.
DavidC

@DavidCarraher: whoops: PI, birinin cevabını kopyaladığımı kastettiğini düşündüm ... yine de bu soru (doğru yorumladıysam), münferit parçaların sadece birbirlerinin permütasyonu olduğu kopyaları kaldırmak için değil parçaların sırasının farklı olduğu, yani {a,bc}daha önceden bulunmuşsa, çıkarmak {a,cb}ama çıkarmak değil {bc,a}. (Ve elbette rahatsız değilim, eğer birisinin cevabını gerçekten / olsaydım / çoğaltırsam, birisi işaret ettiğinde bunu tercih ederim.)
marinus

Haklı görünüyorsun. Talimatlar, kelimelerin sırasını göz ardı etmenin uygun olduğunu söylüyor ("Her harf grubu için yalnızca bir tane listelemesi gerektiğini unutmayın"), ancak gerektirmezler. Bu bana birkaç karakter kazandırabilir. Teşekkürler.
DavidC

2

c99 - 379 gerekli karakterler

#include <stdio.h>
#include <string.h>
#include <ctype.h>
int s(char*w,int l,int m){int b,t=0;for(b=0;b<l;++b){t+=(m&1<<b)?toupper(w[b])-64:0;}return t;}
void p(char*w,int l,int m){for(int b=0;b<l;++b){putchar((m&1<<b)?w[b]:32);}}
int main(){char w[99];gets(w);int i,l=strlen(w),m=(1<<l),t=s(w,l,m-1);
for(i=0;i<m;i++){if(s(w,l,i)==t/2){p(w,l,i);putchar(9);p(w,l,~i);putchar(10);}}}

Yaklaşım oldukça açıktır. Bir maskeye göre sözcükleri toplayan ve bir maskeye göre de yazdıran bir işlev vardır. Standart girişten giriş. Bir gariplik, yazdırma rutininin maskede olmayan harf için boşluk eklemesidir. Grupları ayırmak için bir sekme kullanılır.

Hiçbir bonus ürün yapmıyorum, ne de onları desteklemek için kolayca dönüştürülüyor.

Okunabilir ve yorumlanmış:

#include <stdio.h>
#include <string.h>
#include <ctype.h>
int s(char *w, int l, int m){ /* word, length, mask */
  int b,t=0;                  /* bit and total */
  for (b=0; b<l; ++b){        
/*     printf("Summing %d %d %c %d\n",b,m&(1<<b),w[b],toupper(w[b])-'A'-1); */
    t+=(m&1<<b)?toupper(w[b])-64:0; /* Add to toal if masked (A-1 = @ = 64) */
  }
  return t;
}
void p(char *w, int l, int m){
  for (int b=0; b<l; ++b){ 
    putchar((m&1<<b)?w[b]:32);  /* print if masked (space = 32) */
  }
}
int main(){
  char w[99];
  gets(w);
  int i,l=strlen(w),m=(1<<l),t=s(w,l,m-1);
/*   printf("Word is '%s'\n",w); */
/*   printf("...length %d\n",l); */
/*   printf("...mask   0x%x\n",m-1); */
/*   printf("...total  %d\n",t); */
  for (i=0; i<m; i++){
/*     printf("testing with mask 0x%x...\n",i); */
    if (s(w,l,i)==t/2) {p(w,l,i); putchar(9); p(w,l,~i); putchar(10);}
    /* (tab = 9; newline = 10) */
  }
}

onaylama

 $ wc wordsplit_golf.c
  7  24 385 wordsplit_golf.c
 $ gcc -std=c99 wordsplit_golf.c
 $ echo wordsplit | ./a.out
warning: this program uses gets(), which is unsafe.
 or sp          w  d  lit
wor   l            dsp it
 ords l         w    p it
w    p it        ords l  
   dsp it       wor   l  
w  d  lit        or sp   

1

Ruby: 125 karakter

r=->a{a.reduce(0){|t,c|t+=c.ord-96}}
f=r[w=gets.chomp.chars]
w.size.times{|n|w.combination(n).map{|s|p([s,w-s])if r[s]*2==f}}

Örnek çalışma:

bash-4.2$ ruby -e 'r=->a{a.reduce(0){|t,c|t+=c.ord-96}};f=r[w=gets.chomp.chars.to_a];w.size.times{|p|w.combination(p).map{|q|p([q,w-q])if r[q]*2==f}}' <<< 'wordsplit'
[["w", "o", "r", "l"], ["d", "s", "p", "i", "t"]]
[["w", "p", "i", "t"], ["o", "r", "d", "s", "l"]]
[["o", "r", "s", "p"], ["w", "d", "l", "i", "t"]]
[["w", "d", "l", "i", "t"], ["o", "r", "s", "p"]]
[["o", "r", "d", "s", "l"], ["w", "p", "i", "t"]]
[["d", "s", "p", "i", "t"], ["w", "o", "r", "l"]]

1

Mathematica 123 111

Kelimenin 1/2 "ascii toplamı" olan tüm sözcük alt kümelerini bulur d. Sonra bu altkümelerin tamamlayıcılarını bulur.

d = "WORDSPLIT"

{#, Complement[w, #]}&/@Cases[Subsets@#,x_/;Tr@x==Tr@#/2]&[Sort[ToCharacterCode@d - 64]];
FromCharacterCode[# + 64] & /@ %

{{"IPTW", "DLORS"}, {"LORW", "DIPST"}, {"OPRS", "DILTW"}, {"DILTW", "OPRS"}, {"DIPST", "LORW"} , {"DLORS", "IPTW"}}


1

J, 66 karakter

Olası her alt kümeyi seçmek için base2 sayılarının basamaklarını kullanma.

   f=.3 :'(;~y&-.)"{y#~a#~(=|.)+/"1((+32*0&>)96-~a.i.y)#~a=.#:i.2^#y'
   f 'WordSplit'
┌─────┬─────┐
│Worl │dSpit│
├─────┼─────┤
│Wdlit│orSp │
├─────┼─────┤
│Wpit │ordSl│
├─────┼─────┤
│ordSl│Wpit │
├─────┼─────┤
│orSp │Wdlit│
├─────┼─────┤
│dSpit│Worl │
└─────┴─────┘

0

Benim çözümüm aşağıda. Boyutu neredeyse bir anti-golf, ama çok iyi çalışıyor. N-yönlü bölmeleri destekler (hesaplama süresi yaklaşık 3 bölmeden daha uzun süre çok uzun olsa da) ve yinelenenleri kaldırmayı destekler.

class WordSplitChecker(object):
    def __init__(self, word, splits=2):
        if len(word) == 0:
            raise ValueError, "word too short!"
        if splits == 0:
            raise ValueError, "splits must be > 1; it is impossible to split a word into zero groups"
        self.word = word
        self.splits = splits

    def solve(self, uniq_solutions=False, progress_notifier=True):
        """To solve this problem, we first need to consider all the possible
        rearrangements of a string into two (or more) groups.

        It turns out that this reduces simply to a base-N counting algorithm,
        each digit coding for which group the letter goes into. Obviously
        the longer the word the more digits needed to count up to, so 
        computation time is very long for larger bases and longer words. It 
        could be sped up by using a precalculated array of numbers in the
        required base, but this requires more memory. (Space-time tradeoff.)

        A progress notifier may be set. If True, the default notifier is used,
        if None, no notifier is used, and if it points to another callable,
        that is used. The callable must take the arguments as (n, count, 
        solutions) where n is the number of iterations, count is the total 
        iteration count and solutions is the length of the solutions list. The
        progress notifier is called at the beginning, on every 1000th iteration, 
        and at the end.

        Returns a list of possible splits. If there are no solutions, returns
        an empty list. Duplicate solutions are removed if the uniq_solutions
        parameter is True."""
        if progress_notifier == True:
           progress_notifier = self.progress 
        solutions = []
        bucket = [0] * len(self.word)
        base_tuple = (self.splits,) * len(self.word)
        # The number of counts we need to do is given by: S^N,
        # where S = number of splits,
        #       N = length of word.
        counts = pow(self.splits, len(self.word))
        # xrange does not create a list in memory, so this will work with very
        # little additional memory.
        for i in xrange(counts):
            groups = self.split_word(self.word, self.splits, bucket)
            group_sums = map(self.score_string, groups)
            if len(set(group_sums)) == 1:
                solutions.append(tuple(groups))
            if callable(progress_notifier) and i % 1000 == 0:
                progress_notifier(i, counts, len(solutions))
            # Increment bucket after doing each group; we want to include the
            # null set (all zeroes.)
            bucket = self.bucket_counter(bucket, base_tuple)
        progress_notifier(i, counts, len(solutions))
        # Now we have computed our results we need to remove the results that
        # are symmetrical if uniq_solutions is True.
        if uniq_solutions:
            uniques = []
            # Sort each of the solutions and turn them into tuples.  Then we can 
            # remove duplicates because they will all be in the same order.
            for sol in solutions:
                uniques.append(tuple(sorted(sol)))
            # Use sets to unique the solutions quickly instead of using our
            # own algorithm.
            uniques = list(set(uniques))
            return sorted(uniques)
        return sorted(solutions)

    def split_word(self, word, splits, bucket):
        """Split the word into groups. The digits in the bucket code for the
        groups in which each character goes in to. For example,

        LIONHEAD with a base of 2 and bucket of 00110100 gives two groups, 
        "LIHAD" and "ONE"."""
        groups = [""] * splits
        for n in range(len(word)):
            groups[bucket[n]] += word[n]
        return groups

    def score_string(self, st):
        """Score and sum the letters in the string, A = 1, B = 2, ... Z = 26."""
        return sum(map(lambda x: ord(x) - 64, st.upper()))

    def bucket_counter(self, bucket, carry):
        """Simple bucket counting. Ex.: When passed a tuple (512, 512, 512)
        and a list [0, 0, 0] it increments each column in the list until
        it overflows, carrying the result over to the next column. This could
        be done with fancy bit shifting, but that wouldn't work with very
        large numbers. This should be fine up to huge numbers. Returns a new
        bucket and assigns the result to the passed list. Similar to most
        counting systems the MSB is on the right, however this is an 
        implementation detail and may change in the future.

        Effectively, for a carry tuple of identical values, this implements a 
        base-N numeral system, where N+1 is the value in the tuple."""
        if len(bucket) != len(carry):
            raise ValueError("bucket and carry lists must be the same size")
        # Increase the last column.
        bucket[-1] += 1 
        # Carry numbers. Carry must be propagated by at least the size of the
        # carry list.
        for i in range(len(carry)):
            for coln, col in enumerate(bucket[:]):
                if col >= carry[coln]:
                    # Reset this column, carry the result over to the next.
                    bucket[coln] = 0
                    bucket[coln - 1] += 1
        return bucket

    def progress(self, n, counts, solutions):
        """Display the progress of the solve operation."""
        print "%d / %d (%.2f%%): %d solutions (non-unique)" % (n + 1, counts, (float(n + 1) / counts) * 100, solutions) 

if __name__ == '__main__':
    word = raw_input('Enter word: ')
    groups = int(raw_input('Enter number of required groups: '))
    unique = raw_input('Unique results only? (enter Y or N): ').upper()
    if unique == 'Y':
        unique = True
    else:
        unique = False
    # Start solving.
    print "Start solving"
    ws = WordSplitChecker(word, groups)
    solutions = ws.solve(unique)
    if len(solutions) == 0:
        print "No solutions could be found."
    for solution in solutions:
        for group in solution:
            print group,
        print

Örnek çıktı:

Enter word: wordsplit
Enter number of required groups: 2
Unique results only? (enter Y or N): y
Start solving
1 / 512 (0.20%): 0 solutions (non-unique)
512 / 512 (100.00%): 6 solutions (non-unique)
dspit worl
ordsl wpit
orsp wdlit

1
Kanımca, orijinal sorunun (kod kısalığı) amacına ulaşmaya itiraf etmeden yapılan cevaplar etkili bir şekilde konu dışıdır. Bu cevabın kod-golf ile bir ilişkisi olmadığını itiraf edersiniz, bu yüzden cevap olarak göndermek yerine başka bir yere göndermeyi ve soru üzerine bir yorumda bir bağlantı koymanızı öneririm.
Jeff Swensen

2
@Sugerman: Bu bir referans uygulaması, oyunu kazanma çabası değil. Sayfanın üstünde yer kaplamak yerine referans uygulamaları cevap olarak tercih ediyorum ve bağlantı çürümesi riskini ortadan kaldırmak için bunları yerinde tercih ediyorum.
dmckee --- eski moderatör yavru kedi

@Sugerman: Neden göndermiyorsun? Hiç yoktan iyidir. En aza indirgenebilir, ama neden rahatsız - Kendi sorumu gerçekten kabul edemiyorum (iyi, yapabilirim , ama bunun ruhunda değil.)
Thomas O

Çünkü bunun temelinde, bu bir Soru ve Yanıt sitesidir. Cevap amaçlı olmayan bir şey bu şekilde yayınlanmamalıdır.
Jeff Swensen

1
İlk yorumumda söylediğim gibi, Soruya yapılan bir yorumda veya Sorunun sahibi olduğunuzdan beri oradaki bağlantıyı düzenledim. Tüm diğerlerini (ve oylama sonuçlarını) dikkate almadan kendi Yanıtınızı otomatik olarak kabul etmediği sürece, kendi Sorunuza bir Cevap göndermenin de yanlış bir yanı yoktur.
Jeff Swensen

0

Lua - 195

a=io.read"*l"for i=0,2^#a/2-1 do z,l=0,""r=l for j=1,#a do b=math.floor(i/2^j*2)%2 z=(b*2-1)*(a:byte(j)-64)+z if b>0 then r=r..a:sub(j,j)else l=l..a:sub(j,j)end end if z==0 then print(l,r)end end

giriş büyük harfle yazılmalıdır:

~$ lua wordsplit.lua 
>WORDSPLIT
WDLIT   ORSP
DSPIT   WORL
WPIT    ORDSL

0

Python - 127

w=rawinput()
for q in range(2**len(w)/2):
 a=[0]*2;b=['']*2
 for c in w:a[q%2]+=ord(c)-96;b[q%2]+=c;q/=2
 if a[0]==a[1]:print b

ve burada yinelenmeyen 182 baytlık bir n-bölünmüş Sürüm:

n,w=input()
def b(q):
 a=[0]*n;b=['']*n
 for c in w:a[q%n]+=ord(c)-96;b[q%n]+=c;q/=n
 return a[0]==a[1] and all(b) and frozenset(b)
print set(filter(None,map(b,range(n**len(w)/n))))

Girdi örneğin:

3, 'wordsplit'
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.