สร้างรายการการเรียงสับเปลี่ยนที่เป็นไปได้ทั้งหมดของสตริง


159

ฉันจะไปเกี่ยวกับการสร้างรายการของพีชคณิตที่เป็นไปได้ทั้งหมดของสตริงระหว่างตัวอักษร x และ y ในความยาวที่มีรายการตัวแปรของตัวละคร

ภาษาใดก็ได้ที่ใช้งานได้ แต่ควรเป็นแบบพกพา


คำตอบ:


70

มีหลายวิธีในการทำเช่นนี้ วิธีการทั่วไปใช้การเรียกซ้ำการบันทึกหรือการเขียนโปรแกรมแบบไดนามิก แนวคิดพื้นฐานคือคุณสร้างรายการสตริงทั้งหมดที่มีความยาว 1 จากนั้นในแต่ละการวนซ้ำสำหรับสตริงทั้งหมดที่ผลิตในการทำซ้ำครั้งล่าสุดให้เพิ่มสตริงที่ต่อกันกับแต่ละอักขระในสตริงทีละรายการ (ดัชนีตัวแปรในรหัสด้านล่างติดตามการเริ่มต้นของการทำซ้ำครั้งสุดท้ายและครั้งถัดไป)

บาง pseudocode:

list = originalString.split('')
index = (0,0)
list = [""]
for iteration n in 1 to y:
  index = (index[1], len(list))
  for string s in list.subset(index[0] to end):
    for character c in originalString:
      list.add(s + c)

จากนั้นคุณจะต้องลบสตริงทั้งหมดที่มีความยาวน้อยกว่า x ซึ่งจะเป็นรายการแรก (x-1) * len (originalString) ในรายการ


4
ทำไมต้องเก็บรายการองค์ประกอบก่อนจากนั้นจึงล้างมัน (อ้างถึงบรรทัดที่ 1 และ 3 ในรหัสเทียม)
Håvard Geithus

6
y (บรรทัด 4) คืออะไร
Jaseem

7
@Jaseem จากคำถาม: "การเรียงสับเปลี่ยนที่เป็นไปได้ทั้งหมดของสตริงระหว่างอักขระ x และy ที่มีความยาว"
ck_

39

มันเป็นการดีกว่าที่จะใช้การย้อนรอย

#include <stdio.h>
#include <string.h>

void swap(char *a, char *b) {
    char temp;
    temp = *a;
    *a = *b;
    *b = temp;
}

void print(char *a, int i, int n) {
    int j;
    if(i == n) {
        printf("%s\n", a);
    } else {
        for(j = i; j <= n; j++) {
            swap(a + i, a + j);
            print(a, i + 1, n);
            swap(a + i, a + j);
        }
    }
}

int main(void) {
    char a[100];
    gets(a);
    print(a, 0, strlen(a) - 1);
    return 0;
}

3
ทางออกที่ดีที่สุด everrrrrrrr
GrowinMan

25

คุณจะได้รับจำนวนมากของสายที่แน่นอน ...

\ sum_ {i = x} ^ Y {\ frac {r!} {{(RI)}!}}
เมื่อ x และ y เป็นวิธีที่คุณกำหนดและ r คือจำนวนอักขระที่เราเลือกจาก - หากฉันเข้าใจคุณถูกต้อง แน่นอนคุณควรสร้างสิ่งเหล่านี้ตามความจำเป็นและไม่ทำให้เลอะเทอะและพูดสร้างชุดพลังจากนั้นกรองความยาวของสตริง

ต่อไปนี้ไม่ใช่วิธีที่ดีที่สุดในการสร้างสิ่งเหล่านี้ แต่มันก็เป็นสิ่งที่น่าสนใจไม่น้อยไปกว่ากัน

Knuth (เล่มที่ 4, fascicle 2, 7.2.1.3) บอกเราว่า (s, t) - การรวมกันนั้นเทียบเท่ากับ s + 1 สิ่งที่ถ่ายทีละครั้งด้วยการทำซ้ำ - an (s, t) - การรวมกันเป็นสัญกรณ์ที่ใช้โดย {t \ select {s + t}นูที่เท่ากับ เราสามารถหาสิ่งนี้ได้โดยการสร้างแต่ละครั้งแรก (s, t) - รวมกันในรูปแบบไบนารี (ดังนั้น, ความยาว (s + t)) และนับจำนวน 0 ไปทางซ้ายของแต่ละ 1

10001000011101 -> กลายเป็นการเปลี่ยนแปลง: {0, 3, 4, 4, 4, 1}


15

โซลูชันแบบเรียกซ้ำไม่ใช่ตาม Knuth ตัวอย่างของ Python:

def nextPermutation(perm):
    k0 = None
    for i in range(len(perm)-1):
        if perm[i]<perm[i+1]:
            k0=i
    if k0 == None:
        return None

    l0 = k0+1
    for i in range(k0+1, len(perm)):
        if perm[k0] < perm[i]:
            l0 = i

    perm[k0], perm[l0] = perm[l0], perm[k0]
    perm[k0+1:] = reversed(perm[k0+1:])
    return perm

perm=list("12345")
while perm:
    print perm
    perm = nextPermutation(perm)

2
ที่จริงแล้วสิ่งนี้ไม่ทำงานเมื่อไม่ได้เรียงสตริง ถ้าคุณพยายามที่มี"54321"เพียงหนึ่งสตริงแสดงให้เห็น (ตัวเอง)
tonjo

1
สิ่งที่น่าสนใจก็nextPermutation()คือไร้สัญชาติ - ใช้เพียงอินพุตเพื่อเปลี่ยนรูปแบบและดัชนีจะไม่ได้รับการดูแลตั้งแต่การทำซ้ำจนถึงการทำซ้ำ มันสามารถทำได้โดยสมมติว่าอินพุตเริ่มต้นถูกเรียงลำดับและค้นหาดัชนี ( k0และl0) ตัวเองตามตำแหน่งที่เก็บรักษาไว้ การเรียงลำดับอินพุตเช่น "54321" -> "12345" จะอนุญาตให้อัลกอริทึมนี้ค้นหาการเปลี่ยนลำดับที่คาดไว้ทั้งหมด แต่เนื่องจากมันมีงานพิเศษจำนวนมากในการค้นหาดัชนีเหล่านั้นใหม่สำหรับการเปลี่ยนแปลงทุกครั้งที่สร้างขึ้นจึงมีวิธีที่มีประสิทธิภาพมากขึ้นในการทำสิ่งนี้แบบไม่เกิดซ้ำ
spaaarky21

13

คุณอาจดูที่ " การแจกแจงเซตย่อยของเซต" อย่างมีประสิทธิภาพซึ่งอธิบายถึงอัลกอริธึมที่จะเป็นส่วนหนึ่งของสิ่งที่คุณต้องการ - สร้างชุดย่อยทั้งหมดของอักขระ N อย่างรวดเร็วจากความยาว x ถึง y มันมีการใช้งานใน C.

สำหรับแต่ละเซ็ตย่อยคุณยังต้องสร้างพีชคณิตทั้งหมด ตัวอย่างเช่นหากคุณต้องการ 3 ตัวอักษรจาก "abcde" อัลกอริทึมนี้จะให้ "abc", "abd", "abe" ... แต่คุณต้องอนุญาตให้แต่ละคนได้รับ "acb", "bac", "bca" ฯลฯ


13

โค้ด Java บางอันทำงานตามคำตอบของ Sarp :

public class permute {

    static void permute(int level, String permuted,
                    boolean used[], String original) {
        int length = original.length();
        if (level == length) {
            System.out.println(permuted);
        } else {
            for (int i = 0; i < length; i++) {
                if (!used[i]) {
                    used[i] = true;
                    permute(level + 1, permuted + original.charAt(i),
                       used, original);
                    used[i] = false;
                }
            }
        }
    }

    public static void main(String[] args) {
        String s = "hello";
        boolean used[] = {false, false, false, false, false};
        permute(0, "", used, s);
    }
}

ในฐานะที่เป็นความคิดเห็นโปรดทราบว่าสำหรับสตริงที่มีอักขระซ้ำแล้วซ้ำอีกสิ่งนี้จะไม่สร้างการเรียงสับเปลี่ยนที่ไม่ซ้ำกัน สิ่งนี้สามารถแก้ไขได้ด้วยแฮช แต่นั่นอาจเป็นปัญหากับสตริงที่ยาว
เกล็น

8
คุณอาจต้องการใช้ถ่านอาร์เรย์แทนสตริงเพื่อให้การทำงานนี้เร็วขึ้นเนื่องจากสตริงไม่เปลี่ยนรูปใน java
Abhijeet Kashnia

13

นี่คือทางออกที่ง่ายใน C #

มันสร้างเพียงพีชคณิตแตกต่างกันของสตริงที่กำหนด

    static public IEnumerable<string> permute(string word)
    {
        if (word.Length > 1)
        {

            char character = word[0];
            foreach (string subPermute in permute(word.Substring(1)))
            {

                for (int index = 0; index <= subPermute.Length; index++)
                {
                    string pre = subPermute.Substring(0, index);
                    string post = subPermute.Substring(index);

                    if (post.Contains(character))
                            continue;                       

                    yield return pre + character + post;
                }

            }
        }
        else
        {
            yield return word;
        }
    }

12

มีคำตอบที่ดีมากมายที่นี่ ฉันยังแนะนำวิธีแก้ปัญหาแบบวนซ้ำง่าย ๆ ใน C ++

#include <string>
#include <iostream>

template<typename Consume>
void permutations(std::string s, Consume consume, std::size_t start = 0) {
    if (start == s.length()) consume(s);
    for (std::size_t i = start; i < s.length(); i++) {
        std::swap(s[start], s[i]);
        permutations(s, consume, start + 1);
    }
}

int main(void) {
    std::string s = "abcd";
    permutations(s, [](std::string s) {
        std::cout << s << std::endl;
    });
}

หมายเหตุ : สตริงที่มีอักขระซ้ำจะไม่สร้างการเรียงสับเปลี่ยนที่ไม่ซ้ำกัน


9

ฉันเพิ่งตีอย่างรวดเร็วใน Ruby:

def perms(x, y, possible_characters)
  all = [""]
  current_array = all.clone
  1.upto(y) { |iteration|
    next_array = []
    current_array.each { |string|
      possible_characters.each { |c|
        value = string + c
        next_array.insert next_array.length, value
        all.insert all.length, value
      }
    }
    current_array = next_array
  }
  all.delete_if { |string| string.length < x }
end

คุณอาจมองไปที่ภาษา API สำหรับฟังก์ชั่นการเรียงสับเปลี่ยนและคุณสามารถเขียนโค้ดที่ได้รับการปรับปรุงให้ดีขึ้น แต่ถ้าตัวเลขนั้นสูงมากฉันไม่แน่ใจว่ามีวิธีมากมายที่จะได้ผลลัพธ์มากมาย .

อย่างไรก็ตามแนวคิดเบื้องหลังโค้ดเริ่มต้นด้วยสตริงความยาว 0 จากนั้นติดตามทุกสตริงความยาว Z โดยที่ Z คือขนาดปัจจุบันในการวนซ้ำ จากนั้นผ่านแต่ละสตริงและเพิ่มอักขระแต่ละตัวลงในแต่ละสตริง สุดท้ายให้ลบสิ่งที่ต่ำกว่าขีด จำกัด x และส่งคืนผลลัพธ์

ฉันไม่ได้ทดสอบด้วยอินพุตที่อาจไม่มีความหมาย (รายการตัวละคร null ค่าแปลก ๆ ของ x และ y ฯลฯ )


11
รหัสนี้ผิด มันจะสร้างการเรียงสับเปลี่ยนที่ไม่ถูกต้องเช่นที่มีอักขระซ้ำ ตัวอย่างเช่นสำหรับสตริง "abc" จะสร้างการเรียงสับเปลี่ยนขนาด 3: ["aaa", "aab", "aac", "aba", "abb", "abc", "aca", "acb", "ACC", "baa", "bab", "bac", "bba", "bbb", "bbc", "bca", "bcb", "bcc", "caa", "cab" "," cba "," cbb "," cbc "," cca "," ccb "," ccc "] สิ่งนี้ไม่ถูกต้อง
pmc255

8

นี่คือการแปลเวอร์ชั่นของ Mike's Ruby เป็น Common LISP:

(defun perms (x y original-string)
  (loop with all = (list "")
        with current-array = (list "")
        for iteration from 1 to y
        do (loop with next-array = nil
                 for string in current-array
                 do (loop for c across original-string
                          for value = (concatenate 'string string (string c))
                          do (push value next-array)
                             (push value all))
                    (setf current-array (reverse next-array)))
        finally (return (nreverse (delete-if #'(lambda (el) (< (length el) x)) all)))))

และอีกเวอร์ชั่นสั้นกว่าเล็กน้อยและใช้ฟีเจอร์ลูปสิ่งอำนวยความสะดวกเพิ่มเติม:

(defun perms (x y original-string)
  (loop repeat y
        collect (loop for string in (or (car (last sets)) (list ""))
                      append (loop for c across original-string
                                   collect (concatenate 'string string (string c)))) into sets
        finally (return (loop for set in sets
                              append (loop for el in set when (>= (length el) x) collect el)))))

8

ต่อไปนี้เป็นวิธีแก้ปัญหาแบบเรียกซ้ำคำ C #:

วิธี:

public ArrayList CalculateWordPermutations(string[] letters, ArrayList words, int index)
        {
            bool finished = true;
            ArrayList newWords = new ArrayList();
            if (words.Count == 0)
            {
                foreach (string letter in letters)
                {
                    words.Add(letter);
                }
            }

            for(int j=index; j<words.Count; j++)
            {
                string word = (string)words[j];
                for(int i =0; i<letters.Length; i++)
                {
                    if(!word.Contains(letters[i]))
                    {
                        finished = false;
                        string newWord = (string)word.Clone();
                        newWord += letters[i];
                        newWords.Add(newWord);
                    }
                }
            }

            foreach (string newWord in newWords)
            {   
                words.Add(newWord);
            }

            if(finished  == false)
            {
                CalculateWordPermutations(letters, words, words.Count - newWords.Count);
            }
            return words;
        }

โทรศัพท์:

string[] letters = new string[]{"a","b","c"};
ArrayList words = CalculateWordPermutations(letters, new ArrayList(), 0);

8

... และนี่คือรุ่น C:

void permute(const char *s, char *out, int *used, int len, int lev)
{
    if (len == lev) {
        out[lev] = '\0';
        puts(out);
        return;
    }

    int i;
    for (i = 0; i < len; ++i) {
        if (! used[i])
            continue;

        used[i] = 1;
        out[lev] = s[i];
        permute(s, out, used, len, lev + 1);
        used[i] = 0;
    }
    return;
}

8

ใบอนุญาต (ABC) -> A.perm (BC) -> A.perm [B.perm (C)] -> A.perm [( * B C), (C B * )] -> [( * A BC ), (B A C), (BC A * ), ( * A CB), (C A B), (CB A * )] เพื่อลบรายการที่ซ้ำกันเมื่อแทรกแต่ละตัวอักษรตรวจสอบเพื่อดูว่าสตริงก่อนหน้านี้ลงท้ายด้วยตัวอักษรเดียวกัน (เพราะเหตุใด - ออกกำลังกาย)

public static void main(String[] args) {

    for (String str : permStr("ABBB")){
        System.out.println(str);
    }
}

static Vector<String> permStr(String str){

    if (str.length() == 1){
        Vector<String> ret = new Vector<String>();
        ret.add(str);
        return ret;
    }

    char start = str.charAt(0);
    Vector<String> endStrs = permStr(str.substring(1));
    Vector<String> newEndStrs = new Vector<String>();
    for (String endStr : endStrs){
        for (int j = 0; j <= endStr.length(); j++){
            if (endStr.substring(0, j).endsWith(String.valueOf(start)))
                break;
            newEndStrs.add(endStr.substring(0, j) + String.valueOf(start) + endStr.substring(j));
        }
    }
    return newEndStrs;
}

พิมพ์พีชคณิตทั้งหมดซ้ำซ้อน


8

โซลูชันแบบเรียกซ้ำใน C ++

int main (int argc, char * const argv[]) {
        string s = "sarp";
        bool used [4];
        permute(0, "", used, s);
}

void permute(int level, string permuted, bool used [], string &original) {
    int length = original.length();

    if(level == length) { // permutation complete, display
        cout << permuted << endl;
    } else {
        for(int i=0; i<length; i++) { // try to add an unused character
            if(!used[i]) {
                used[i] = true;
                permute(level+1, original[i] + permuted, used, original); // find the permutations starting with this string
                used[i] = false;
            }
        }
}

7

ใน Perl หากคุณต้องการ จำกัด ตัวคุณให้เป็นตัวอักษรตัวเล็กคุณสามารถทำสิ่งนี้ได้:

my @result = ("a" .. "zzzz");

สิ่งนี้ให้สตริงที่เป็นไปได้ทั้งหมดระหว่าง 1 ถึง 4 อักขระโดยใช้อักขระตัวพิมพ์เล็ก สำหรับพิมพ์ใหญ่เปลี่ยนแปลง"a"ไป"A"และ"zzzz"การ"ZZZZ"การ

สำหรับกรณีผสมมันยากขึ้นมากและอาจไม่สามารถทำได้กับหนึ่งในผู้ประกอบการในตัวของ Perl เช่นนั้น


7

คำตอบทับทิมที่ใช้งานได้:

class String
  def each_char_with_index
    0.upto(size - 1) do |index|
      yield(self[index..index], index)
    end
  end
  def remove_char_at(index)
    return self[1..-1] if index == 0
    self[0..(index-1)] + self[(index+1)..-1]
  end
end

def permute(str, prefix = '')
  if str.size == 0
    puts prefix
    return
  end
  str.each_char_with_index do |char, index|
    permute(str.remove_char_at(index), prefix + char)
  end
end

# example
# permute("abc")

สำหรับผู้ชื่นชอบซับใน Ruby: stackoverflow.com/questions/5773961/ …
dojosto

6
import java.util.*;

public class all_subsets {
    public static void main(String[] args) {
        String a = "abcd";
        for(String s: all_perm(a)) {
            System.out.println(s);
        }
    }

    public static Set<String> concat(String c, Set<String> lst) {
        HashSet<String> ret_set = new HashSet<String>();
        for(String s: lst) {
            ret_set.add(c+s);
        }
        return ret_set;
    }

    public static HashSet<String> all_perm(String a) {
        HashSet<String> set = new HashSet<String>();
        if(a.length() == 1) {
            set.add(a);
        } else {
            for(int i=0; i<a.length(); i++) {
                set.addAll(concat(a.charAt(i)+"", all_perm(a.substring(0, i)+a.substring(i+1, a.length()))));
            }
        }
        return set;
    }
}

6

การเรียกซ้ำ Java ต่อไปนี้จะพิมพ์การเปลี่ยนลำดับทั้งหมดของสตริงที่กำหนด:

//call it as permut("",str);

public void permut(String str1,String str2){
    if(str2.length() != 0){
        char ch = str2.charAt(0);
        for(int i = 0; i <= str1.length();i++)
            permut(str1.substring(0,i) + ch + str1.substring(i,str1.length()),
                     str2.substring(1,str2.length()));
    }else{
    System.out.println(str1);
    }
}

ต่อไปนี้เป็นรุ่นที่อัปเดตของวิธีการ "permut" ด้านบนซึ่งทำให้ n! (n แฟคทอเรียล) การโทรซ้ำน้อยกว่าเมื่อเทียบกับวิธีการข้างต้น

//call it as permut("",str);

public void permut(String str1,String str2){
   if(str2.length() > 1){
       char ch = str2.charAt(0);
       for(int i = 0; i <= str1.length();i++)
          permut(str1.substring(0,i) + ch + str1.substring(i,str1.length()),
                 str2.substring(1,str2.length()));
   }else{
    char ch = str2.charAt(0);
    for(int i = 0; i <= str1.length();i++)
        System.out.println(str1.substring(0,i) + ch +    str1.substring(i,str1.length()),
                 str2.substring(1,str2.length()));
   }
}

นี่เป็นคำตอบที่สะอาดที่สุดและฉันเชื่อว่าฉันเคยเห็นมาก่อนในหนังสือ "Cracking the Coding Interview"
Tao Zhang

1
@TaoZhang ขอบคุณสำหรับส่วนเสริมฉันไม่ได้คัดลอกมาจากที่ใดก็ได้ แต่เป็นไปได้ว่ามีคนสร้างอัลโก้แบบเดียวกัน อย่างไรก็ตามฉันได้อัปเดตโค้ดด้านบนสำหรับการโทรซ้ำน้อย
Ramy

5

ฉันไม่แน่ใจว่าทำไมคุณต้องการทำสิ่งนี้ตั้งแต่แรก ชุดผลลัพธ์สำหรับค่าใด ๆ ที่มีขนาดใหญ่ปานกลางของ x และ y จะมีขนาดใหญ่และจะเพิ่มขึ้นอย่างทวีคูณเมื่อ x และ / หรือ y ใหญ่ขึ้น

ให้บอกว่าชุดอักขระที่เป็นไปได้ของคุณคือตัวอักษรตัวพิมพ์เล็ก 26 ตัวและคุณขอให้แอปพลิเคชันของคุณสร้างพีชคณิตทั้งหมดที่ length = 5 สมมติว่าคุณไม่มีหน่วยความจำหมดคุณจะได้ 11,881,376 (เช่น 26 กำลัง) จาก 5) สตริงกลับ ชนที่ความยาวสูงสุด 6 และคุณจะได้รับ 308,915,776 สตริงกลับ ตัวเลขเหล่านี้มีขนาดใหญ่ขึ้นอย่างเจ็บปวดอย่างรวดเร็ว

นี่เป็นวิธีแก้ปัญหาที่ฉันรวบรวมไว้ใน Java คุณจะต้องจัดเตรียมอาร์กิวเมนต์สองตัว (สอดคล้องกับ x และ y) มีความสุข.

public class GeneratePermutations {
    public static void main(String[] args) {
        int lower = Integer.parseInt(args[0]);
        int upper = Integer.parseInt(args[1]);

        if (upper < lower || upper == 0 || lower == 0) {
            System.exit(0);
        }

        for (int length = lower; length <= upper; length++) {
            generate(length, "");
        }
    }

    private static void generate(int length, String partial) {
        if (length <= 0) {
            System.out.println(partial);
        } else {
            for (char c = 'a'; c <= 'z'; c++) {
                generate(length - 1, partial + c);
            }
        }
    }
}

นาน แต่คุณไม่ได้สร้างพวกเขาด้วยการทำซ้ำ?
Kakira

5

นี่คือเวอร์ชันที่ไม่ใช่แบบเรียกซ้ำฉันมาด้วยใน javascript มันไม่ได้มาจาก Knuth ที่ไม่ใช่แบบเรียกซ้ำข้างต้นแม้ว่าจะมีความคล้ายคลึงกันในการสลับองค์ประกอบ ฉันตรวจสอบความถูกต้องแล้วสำหรับอาร์เรย์อินพุทสูงสุด 8 องค์ประกอบ

การเพิ่มประสิทธิภาพอย่างรวดเร็วจะเป็นการเตรียมoutอาร์เรย์ล่วงหน้าและหลีกเลี่ยงpush()อาร์เรย์และหลีกเลี่ยง

แนวคิดพื้นฐานคือ:

  1. ให้อาเรย์แหล่งเดียวสร้างอาร์เรย์ใหม่ชุดแรกซึ่งสลับองค์ประกอบแรกกับแต่ละองค์ประกอบตามลำดับในแต่ละครั้งที่ปล่อยให้องค์ประกอบอื่นไม่ถูกรบกวน เช่นได้รับ 1234 สร้าง 1234, 2134, 3214, 4231

  2. ใช้แต่ละอาร์เรย์จากรหัสผ่านก่อนหน้าเป็นเมล็ดสำหรับรหัสผ่านใหม่ แต่แทนที่จะสลับองค์ประกอบแรกให้สลับองค์ประกอบที่สองกับแต่ละองค์ประกอบที่ตามมา นอกจากนี้ในเวลานี้อย่ารวมอาร์เรย์ดั้งเดิมในเอาต์พุต

  3. ทำซ้ำขั้นตอนที่ 2 จนกระทั่งเสร็จสิ้น

นี่คือตัวอย่างโค้ด:

function oxe_perm(src, depth, index)
{
    var perm = src.slice();     // duplicates src.
    perm = perm.split("");
    perm[depth] = src[index];
    perm[index] = src[depth];
    perm = perm.join("");
    return perm;
}

function oxe_permutations(src)
{
    out = new Array();

    out.push(src);

    for (depth = 0; depth < src.length; depth++) {
        var numInPreviousPass = out.length;
        for (var m = 0; m < numInPreviousPass; ++m) {
            for (var n = depth + 1; n < src.length; ++n) {
                out.push(oxe_perm(out[m], depth, n));
            }
        }
    }

    return out;
}

3

ในทับทิม:

str = "a"
100_000_000.times {puts str.next!}

มันค่อนข้างเร็ว แต่มันจะใช้เวลาสักครู่ =) แน่นอนคุณสามารถเริ่มต้นที่ "aaaaaaaa" หากสตริงสั้น ๆ ไม่น่าสนใจสำหรับคุณ

ฉันอาจตีความคำถามที่เกิดขึ้นจริงผิด - ในหนึ่งในโพสต์มันฟังราวกับว่าคุณเพิ่งต้องการห้องสมุด bruteforce ของสตริง แต่ในคำถามหลักดูเหมือนว่าคุณจะต้องอนุญาตให้ใช้สตริงที่เฉพาะเจาะจง

ปัญหาของคุณคล้ายกับปัญหานี้: http://beust.com/weblog/archives/000491.html (รายการจำนวนเต็มทั้งหมดที่ไม่มีตัวเลขซ้ำตัวเองซึ่งส่งผลให้ภาษาจำนวนมากแก้ปัญหาด้วย ผู้ชาย ocaml ใช้วิธีเรียงสับเปลี่ยนและผู้ชาย java บางคนใช้อีกวิธีหนึ่ง)


ปัญหาเกี่ยวกับข้อเสนอของคุณคือ str.next! จะไม่ซ้ำกับอักขระที่พิมพ์ได้ทั้งหมด ตัวอย่างของคุณจะสร้างอักษรตัวพิมพ์เล็กเท่านั้น - ไม่มีเครื่องหมายวรรคตอนหรือตัวพิมพ์ใหญ่
Jarsen

3

ฉันต้องการสิ่งนี้ในวันนี้และแม้ว่าคำตอบที่ได้รับนั้นชี้ให้ฉันไปในทิศทางที่ถูกต้อง แต่พวกเขาก็ไม่ได้เป็นอย่างที่ฉันต้องการ

นี่คือการใช้งานโดยใช้วิธีการของฮีป ความยาวของอาเรย์จะต้องมีอย่างน้อย 3 และสำหรับการพิจารณาในทางปฏิบัติต้องไม่มากกว่า 10 หรือมากกว่านั้นขึ้นอยู่กับสิ่งที่คุณต้องการจะทำความอดทนและความเร็วสัญญาณนาฬิกา

ก่อนที่คุณจะเข้าสู่ลูปของคุณเริ่มต้นPerm(1 To N)ด้วยการเปลี่ยนแปลงครั้งแรกStack(3 To N)กับศูนย์ * และLevelด้วย2 ** ในตอนท้ายของการเรียกวนรอบNextPermซึ่งจะส่งกลับเท็จเมื่อเราทำเสร็จแล้ว

* VB จะทำเพื่อคุณ

** คุณสามารถเปลี่ยน NextPerm ได้เล็กน้อยเพื่อทำให้สิ่งนี้ไม่จำเป็น แต่ก็ชัดเจนขึ้นเช่นนี้

Option Explicit

Function NextPerm(Perm() As Long, Stack() As Long, Level As Long) As Boolean
Dim N As Long
If Level = 2 Then
    Swap Perm(1), Perm(2)
    Level = 3
Else
    While Stack(Level) = Level - 1
        Stack(Level) = 0
        If Level = UBound(Stack) Then Exit Function
        Level = Level + 1
    Wend
    Stack(Level) = Stack(Level) + 1
    If Level And 1 Then N = 1 Else N = Stack(Level)
    Swap Perm(N), Perm(Level)
    Level = 2
End If
NextPerm = True
End Function

Sub Swap(A As Long, B As Long)
A = A Xor B
B = A Xor B
A = A Xor B
End Sub

'This is just for testing.
Private Sub Form_Paint()
Const Max = 8
Dim A(1 To Max) As Long, I As Long
Dim S(3 To Max) As Long, J As Long
Dim Test As New Collection, T As String
For I = 1 To UBound(A)
    A(I) = I
Next
Cls
ScaleLeft = 0
J = 2
Do
    If CurrentY + TextHeight("0") > ScaleHeight Then
        ScaleLeft = ScaleLeft - TextWidth(" 0 ") * (UBound(A) + 1)
        CurrentY = 0
        CurrentX = 0
    End If
    T = vbNullString
    For I = 1 To UBound(A)
        Print A(I);
        T = T & Hex(A(I))
    Next
    Print
    Test.Add Null, T
Loop While NextPerm(A, S, J)
J = 1
For I = 2 To UBound(A)
    J = J * I
Next
If J <> Test.Count Then Stop
End Sub

วิธีอื่นอธิบายโดยผู้เขียนหลายคน Knuth อธิบายสองข้อหนึ่งจัดลำดับคำศัพท์ แต่ซับซ้อนและช้าอีกอันเรียกว่าวิธีการเปลี่ยนแปลงแบบธรรมดา Jie Gao และ Dianjun Wang ก็เขียนบทความที่น่าสนใจเช่นกัน


2

รหัสนี้ในไพ ธ อนเมื่อถูกเรียกด้วยallowed_charactersset to [0,1]และสูงสุด 4 ตัวอักษรจะสร้างผลลัพธ์ 2 ^ 4:

['0000', '0001', '0010', '0011', '0100', '0101', '0110', '0111', '1000', '1001', '1010', '1011', '1100', '1101', '1110', '1111']

def generate_permutations(chars = 4) :

#modify if in need!
    allowed_chars = [
        '0',
        '1',
    ]

    status = []
    for tmp in range(chars) :
        status.append(0)

    last_char = len(allowed_chars)

    rows = []
    for x in xrange(last_char ** chars) :
        rows.append("")
        for y in range(chars - 1 , -1, -1) :
            key = status[y]
            rows[x] = allowed_chars[key] + rows[x]

        for pos in range(chars - 1, -1, -1) :
            if(status[pos] == last_char - 1) :
                status[pos] = 0
            else :
                status[pos] += 1
                break;

    return rows

import sys


print generate_permutations()

หวังว่านี่เป็นประโยชน์กับคุณ ทำงานร่วมกับตัวละครใด ๆ ไม่เพียง แต่ตัวเลข


นี่ไม่ใช่การเรียงสับเปลี่ยน แต่เป็นการเลือกชุดย่อยเช่น ABC & 001 = C ในขณะที่การเรียงสับเปลี่ยนที่ถูกต้องจะต้องมีทั้งสามตัวอักษร
Schultz9999

เอ่อ? ขอโทษฉันไม่เข้าใจสิ่งที่คุณพูด หากคุณแก้ไขมันให้เป็นเวอร์ชั่นที่แก้ไขได้ฉันจะคอม
มิวนิตี้


0

แม้ว่าสิ่งนี้จะไม่ตอบคำถามของคุณอย่างแน่นอน แต่นี่เป็นวิธีหนึ่งในการสร้างการเรียงสับเปลี่ยนของตัวอักษรจากสตริงที่มีความยาวเท่ากันเช่นถ้าคำของคุณคือ "กาแฟ" "joomla" และ "moodle" คุณสามารถ คาดหวังผลลัพธ์เช่น "coodle", "joodee", "joffle" ฯลฯ

โดยพื้นฐานแล้วจำนวนชุดค่าผสมคือ (จำนวนคำ) ต่อพลังของ (จำนวนตัวอักษรต่อคำ) ดังนั้นเลือกตัวเลขสุ่มระหว่าง 0 และจำนวนชุดค่าผสม - 1 แปลงตัวเลขนั้นเป็นฐาน (จำนวนคำ) จากนั้นใช้ตัวเลขแต่ละตัวของตัวเลขนั้นเป็นตัวบ่งชี้สำหรับคำที่จะใช้อักษรตัวถัดไป

เช่นในตัวอย่างด้านบน 3 คำ, 6 ตัวอักษร = 729 รวมกัน เลือกตัวเลขสุ่ม: 465 แปลงเป็นฐาน 3: 122020 ใช้ตัวอักษรตัวแรกจากคำ 1, 2 จากคำ 2, 3 จากคำ 2, 4 จากคำ 0 ... และคุณจะได้รับ ... "joofle"

หากคุณต้องการการเรียงสับเปลี่ยนทั้งหมดเพียงแค่วนจาก 0 ถึง 728 แน่นอนถ้าคุณแค่เลือกค่าแบบสุ่มหนึ่งวิธีที่สับสนน้อยกว่าง่ายกว่าคือวนรอบตัวอักษร วิธีนี้ช่วยให้คุณหลีกเลี่ยงการเรียกซ้ำถ้าคุณต้องการการเปลี่ยนลำดับทั้งหมดรวมทั้งทำให้คุณดูเหมือนว่าคุณรู้คณิตศาสตร์(tm) !

หากจำนวนชุดค่าผสมมากเกินไปคุณสามารถแบ่งเป็นชุดคำที่เล็กลงและต่อท้ายได้


0

c # ซ้ำ:

public List<string> Permutations(char[] chars)
    {
        List<string> words = new List<string>();
        words.Add(chars[0].ToString());
        for (int i = 1; i < chars.Length; ++i)
        {
            int currLen = words.Count;
            for (int j = 0; j < currLen; ++j)
            {
                var w = words[j];
                for (int k = 0; k <= w.Length; ++k)
                {
                    var nstr = w.Insert(k, chars[i].ToString());
                    if (k == 0)
                        words[j] = nstr;
                    else
                        words.Add(nstr);
                }
            }
        }
        return words;
    }

0
def gen( x,y,list): #to generate all strings inserting y at different positions
list = []
list.append( y+x )
for i in range( len(x) ):
    list.append( func(x,0,i) + y + func(x,i+1,len(x)-1) )
return list 

def func( x,i,j ): #returns x[i..j]
z = '' 
for i in range(i,j+1):
    z = z+x[i]
return z 

def perm( x , length , list ): #perm function
if length == 1 : # base case
    list.append( x[len(x)-1] )
    return list 
else:
    lists = perm( x , length-1 ,list )
    lists_temp = lists #temporarily storing the list 
    lists = []
    for i in range( len(lists_temp) ) :
        list_temp = gen(lists_temp[i],x[length-2],lists)
        lists += list_temp 
    return lists

0
def permutation(str)
  posibilities = []
  str.split('').each do |char|
    if posibilities.size == 0
      posibilities[0] = char.downcase
      posibilities[1] = char.upcase
    else
      posibilities_count = posibilities.length
      posibilities = posibilities + posibilities
      posibilities_count.times do |i|
        posibilities[i] += char.downcase
        posibilities[i+posibilities_count] += char.upcase
      end
    end
  end
  posibilities
end

นี่คือสิ่งที่ฉันทำในเวอร์ชันที่ไม่ต้องเรียกซ้ำ



0

ทีนี้นี่คือทางออก O (n!) ที่สง่างามและไม่ซ้ำซาก:

public static StringBuilder[] permutations(String s) {
        if (s.length() == 0)
            return null;
        int length = fact(s.length());
        StringBuilder[] sb = new StringBuilder[length];
        for (int i = 0; i < length; i++) {
            sb[i] = new StringBuilder();
        }
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            int times = length / (i + 1);
            for (int j = 0; j < times; j++) {
                for (int k = 0; k < length / times; k++) {
                    sb[j * length / times + k].insert(k, ch);
                }
            }
        }
        return sb;
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.