2008-11-23 6 views
7

Ich versuche, einen Algorithmus in C# zu erstellen, die folgende Ausgabe Strings erzeugt:Was ist ein guter Weg, um herauszufinden, alle möglichen Wörter einer bestimmten Länge

AAAA 
AAAB 
AAAC 
...and so on... 
ZZZX 
ZZZY 
ZZZZ 

Was ist der beste Weg, dies zu erreichen?

public static IEnumerable<string> GetWords() 
{ 
    //Perform algorithm 
    yield return word; 
} 
+0

erzeugt Was Sie versuchen zu tun? Es könnte besser sein, die Liste abhängig von Ihrer Antwort zu erstellen. –

+0

@John the Statistician: Die Verwendung von Iteratorblöcken * erzeugt * die Liste träge. –

+0

Dies kann beim Erstellen von naiven Brute-Force-Logik nützlich sein. Ich habe etwas Ähnliches für eine Klasse gemacht, einmal, wo wir eine Chiffre brechen mussten. Die analytische Technik war einfach, also schrieb ich auch ein Programm, das das ganze Computerlabor für ein paar Stunden am frühen Samstagmorgen verwendete. :) –

Antwort

17

gut, wenn die Länge ein konstantes 4 ist, dann würde dies es handhaben:

public static IEnumerable<String> GetWords() 
{ 
    for (Char c1 = 'A'; c1 <= 'Z'; c1++) 
    { 
     for (Char c2 = 'A'; c2 <= 'Z'; c2++) 
     { 
      for (Char c3 = 'A'; c3 <= 'Z'; c3++) 
      { 
       for (Char c4 = 'A'; c4 <= 'Z'; c4++) 
       { 
        yield return "" + c1 + c2 + c3 + c4; 
       } 
      } 
     } 
    } 
} 

falls die Länge ein Parameter ist, diese rekursiven Lösung wäre es handhaben:

public static IEnumerable<String> GetWords(Int32 length) 
{ 
    if (length <= 0) 
     yield break; 

    for (Char c = 'A'; c <= 'Z'; c++) 
    { 
     if (length > 1) 
     { 
      foreach (String restWord in GetWords(length - 1)) 
       yield return c + restWord; 
     } 
     else 
      yield return "" + c; 
    } 
} 
+0

Ich habe das fast abgelehnt, sobald ich den Text im ersten Vorschlag gesehen habe. Der zweite scheint in Ordnung zu sein. – Svante

15

Es gibt immer die obligatorische LINQ-Implementierung. Wahrscheinlich Quatsch-Performance, aber seit wann hinderte die Performance neue coole Features?

var letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray(); 

var sequence = from one in letters 
       from two in letters 
       from three in letters 
       from four in letters 
       orderby one, two, three, four 
       select new string(new[] { one, two, three, four }); 

'Sequenz' wird jetzt ein IQueryable, das AAAA zu ZZZZ enthält.

Edit:

Ok, so war es nervt mich, dass es möglich sein sollte, mit LINQ eine Folge von konfigurierbaren Länge mit einem konfigurierbaren Alphabet zu machen. Hier ist es also. Wiederum völlig sinnlos, aber es nervte mich.

public void Nonsense() 
{ 
    var letters = new[]{"A","B","C","D","E","F", 
         "G","H","I","J","K","L", 
         "M","N","O","P","Q","R","S", 
         "T","U","V","W","X","Y","Z"}; 

    foreach (var val in Sequence(letters, 4)) 
     Console.WriteLine(val); 
} 

private IQueryable<string> Sequence(string[] alphabet, int size) 
{ 
    // create the first level 
    var sequence = alphabet.AsQueryable(); 

    // add each subsequent level 
    for (var i = 1; i < size; i++) 
     sequence = AddLevel(sequence, alphabet); 

    return from value in sequence 
      orderby value 
      select value; 
} 

private IQueryable<string> AddLevel(IQueryable<string> current, string[] characters) 
{ 
    return from one in current 
      from character in characters 
      select one + character; 
} 

Der Aufruf der Sequenz Methode erzeugt die gleiche AAAA zu ZZZZ Liste wie vorher, aber jetzt können Sie das Wörterbuch ändern verwendet und wie lange die erzeugten Worte sein werden.

+0

Wenn diese Lösung richtig ist, ist es eine erstaunliche Lösung. Danke für den C# Einblick. Ich muss eines der Jon Skeet Bücher kaufen, wenn er über C# 5.0 mit Metaprogrammierung schreibt :) –

1

Inspiriert von Garry Shutlers Antwort entschied ich, seine Antwort in T-SQL zu rekodieren.

Sagen Sie "Briefe" ist eine Tabelle mit nur einem Feld, MyChar, ein CHAR (1). Es hat 26 Zeilen, jeweils einen Buchstaben des Alphabets. So hätten wir (Sie können diesen Code auf SQL Server copy-paste und laufen, wie sie ist, sie in Aktion zu sehen):

DECLARE @Letters TABLE (
    MyChar CHAR(1) PRIMARY KEY 
) 
DECLARE @N INT 
SET @N=0 
WHILE @N<26 BEGIN 
    INSERT @Letters (MyChar) VALUES (CHAR(@N + 65)) 
    SET @N = @N + 1 
END 
-- SELECT * FROM @Letters ORDER BY 1 

SELECT A.MyChar, B.MyChar, C.MyChar, D.MyChar 
FROM @Letters A, Letters B, Letters C, Letters D 
ORDER BY 1,2,3,4 

Die Vorteile sind: Es ist leicht erweiterbar in mit Kapital/Kleinschreibung oder mit Nicht-englische lateinische Zeichen (denken Sie an "Ñ" oder Cedille, Eszets und Ähnliches) und Sie würden immer noch eine geordnete Menge bekommen, nur müssen Sie eine Kollatierung hinzufügen. Plus SQL Server führt dies etwas schneller als LINQ auf einer einzelnen Kernmaschine aus, auf Multicore (oder Multiprozessoren) kann die Ausführung parallel sein und noch mehr Schub bekommen.

Leider ist es für den Fall mit 4 Buchstaben stecken geblieben. Die rekursive Lösung von lassevk ist allgemeiner, der Versuch, eine allgemeine Lösung in T-SQL zu erstellen, würde notwendigerweise dynamisches SQL mit all seinen Gefahren bedeuten.

+0

Sie können Haskell nicht schlagen: print [a: b: c: d: [] | Sei q = ['a' .. 'z'], a <- q, b <- q, c <- q, d <- q] –

+0

@Joe Pineda Ihre Lösung wird wegen "ORDER BY 1" niemals DCBA generieren , 2,3,4 " – xxxxxxx

+0

Ja, tut es! Ich habe gerade überprüft, indem ich den Code auf SQL S. 2000: Sequenz "DCBA" ist Zeile 54107. Alle möglichen Kombinationen sind in der Tat produziert, ich erweitern den Code, so dass es einfacher zu testen ist. –

0

Javascript!

var chars = 4, abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ", top = 1, fact = []; 
for (i = 0; i < chars; i++) { fact.unshift(top); top *= abc.length; } 
for (i = 0; i < top; i++) 
{ 
    for (j = 0; j < chars; j++) 
     document.write(abc[Math.floor(i/fact[j]) % abc.length]); 
    document.write("<br \>\n"); 
} 
+0

das ist nett, also berechnen Sie zuerst die Anzahl der möglichen Wörter oben. und Sie sehen die Zeichen als Zahlen in der Basis abc.length :) Ich dachte vor einiger Zeit daran, es ist eine nette Idee :) und auch besser als die rekursive Ansatz, obwohl Division und Modulo ihren Tribut fordern könnten – xxxxxxx

1

Python!

(Dies ist nur ein Hack, nicht‘nimm mich zu ernst :-)

# Convert a number to the base 26 using [A-Z] as the cyphers 
def itoa26(n): 
    array = [] 
    while n: 
     lowestDigit = n % 26 
     array.append(chr(lowestDigit + ord('A'))) 
     n /= 26 
    array.reverse() 
    return ''.join(array) 

def generateSequences(nChars): 
    for n in xrange(26**nChars): 
     string = itoa26(n) 
     yield 'A'*(nChars - len(string)) + string 

for string in generateSequences(3): 
    print string 
1

Haskell!

replicateM 4 ['A'..'Z'] 

Ruby!

('A'*4..'Z'*4).to_a 
2

GNU Bash!

{a..z}{a..z}{a..z}{a..z} 
5

Nur coment zu Garry Shutler, aber ich möchte Code Färbung:

Sie wirklich nicht brauchen, es IQuaryable zu machen, weder die Art, so dass Sie die zweite Methode entfernen können. Ein Schritt forwad ist Aggregat für das Kreuzprodukt zu verwenden, ist es am Ende wie folgt:

IEnumerable<string> letters = new[]{ 
       "A","B","C","D","E","F",      
       "G","H","I","J","K","L", 
       "M","N","O","P","Q","R","S",   
       "T","U","V","W","X","Y","Z"}; 

var result = Enumerable.Range(0, 4) 
       .Aggregate(letters, (curr, i) => curr.SelectMany(s => letters, (s, c) => s + c)); 

foreach (var val in result) 
    Console.WriteLine(val); 

Anders sollte einen Nobelpreis für die Linq Sache bekommen!

0

Verwenden etwas, das für jede einzelne Buchstabenkombination automatisch Googles, dann sehen, ob es mehr „.sz“ oder „.af“ ist die Treffer dann „.com“ trifft auf den fünf ersten Ergebnissen ...;)

im Ernst, was Sie suchen könnte Tries (Datenstruktur), obwohl Sie noch brauchen die Sache zu füllen, die wahrscheinlich viel schwieriger ist ...

1

Dies ist eine rekursive Version der gleichen Funktionen in C#:

using System; 
using System.Collections.Generic; 
using System.Text; 
using System.IO; 

namespace ConsoleApplication1Test 
{ 
    class Program 
    { 
     static char[] my_func(char[] my_chars, int level) 
     { 
      if (level > 1) 
       my_func(my_chars, level - 1); 
      my_chars[(my_chars.Length - level)]++; 
      if (my_chars[(my_chars.Length - level)] == ('Z' + 1)) 
      { 
       my_chars[(my_chars.Length - level)] = 'A'; 
       return my_chars; 
      } 
      else 
      { 
       Console.Out.WriteLine(my_chars); 
       return my_func(my_chars, level); 
      } 
     } 
     static void Main(string[] args) 
     { 
      char[] text = { 'A', 'A', 'A', 'A' }; 
      my_func(text,text.Length); 
      Console.ReadKey(); 
     } 
    } 
} 

Ausdrucke von AAAA bis ZZZZ

1

Simpler Python!

def getWords(length=3): 
    if length == 0: raise StopIteration 
    for letter in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ': 
     if length == 1: yield letter 
     else: 
      for partialWord in getWords(length-1): 
       yield letter+partialWord 
+0

Danke, für mich Python funktioniert genauso gut. – r1k0

0

Ein sehr einfacher, aber genial Code, der alle Worte von 3 und 4 Buchstaben Englisch Sprache

#include <iostream> 
using namespace std; 
char alpha[26]={'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'} 
int main() { 
int num; 
cin >> num; 
if (num == 3) { //all 3 letter words 
    for (int i = 0; i <= 25; i++) { 
     for (int o = 0; o <= 25; o++) { 
      for (int p = 0; p <= 25; p++) { 
       cout << alpha[i] << alpha[o] << alpha[p] << " "; 
      } 
     } 
    } 
} 
else if (num == 4) { //all 4 letter words 
    for (int i = 0; i <= 25; i++) { 
     for (int o = 0; o <= 25; o++) { 
      for (int p = 0; p <= 25; p++) { 
       for (int q = 0; q <= 25; q++) { 
        cout << alpha[i] << alpha[o] << alpha[p] << alpha[q] << " "; 
       } 
      } 
     } 
    } 
} 
else { 
    cout << "Not more than 4"; //it will take more than 2 hours for generating all 5 letter words 
    } 
}