Відповіді:
Існує кілька способів зробити це. Загальні методи використовують рекурсію, запам'ятовування або динамічне програмування. Основна ідея полягає в тому, що ви створюєте список усіх рядків довжиною 1, після чого в кожній ітерації для всіх рядків, отриманих за останню ітерацію, додаєте ту рядок, з'єднаний з кожним символом в рядку окремо. (змінний індекс у наведеному нижче коді відстежує початок останньої та наступної ітерації)
Деякі псевдокоди:
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) записами у списку.
Краще використовувати зворотний трек
#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;
}
Ви отримаєте багато струн, це точно ...
Де x і y - це те, як ви їх визначаєте, а r - кількість символів, з яких вибираємо - якщо я вас правильно розумію. Ви обов'язково повинні генерувати їх у міру необхідності, а не скупитися і говорити, генерувати живлення і потім фільтрувати довжину рядків.
Наступне, безумовно, не найкращий спосіб їх отримання, але це цікаво, окрім цього.
Кнут (том 4, фрагмент 2, 7.2.1.3) говорить нам, що (s, t) -комбінація еквівалентна s + 1 речам, взятим t за один раз з повторенням - (s, t) -комбінація - це позначення, якими користується Кнут, що дорівнює . Ми можемо це зрозуміти, спершу генеруючи кожну (s, t) -комбінацію у бінарній формі (так, довжини (s + t)) і підраховуючи число 0 у лівій частині кожного 1.
10001000011101 -> стає перестановкою: {0, 3, 4, 4, 4, 1}
Нерекурсивне рішення за прикладом 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)
"54321"
лише ОДИН, рядок відображається (сама).
nextPermutation()
без стану - для перестановки потрібен лише вхід, а індекси не підтримуються від ітерації до ітерації. Це можна зробити, якщо припустити, що початковий вхід був сортований і сам знаходив індекси ( k0
і l0
), виходячи з того, де підтримується впорядкування. Сортування введення типу "54321" -> "12345" дозволило б цьому алгоритму знайти всі очікувані перестановки. Але оскільки для того, щоб знову знайти ці індекси для кожної створеної перестановки, існує велика кількість додаткової роботи, існують більш ефективні способи зробити це нерекурсивно.
Ви можете подивитися " Ефективне перерахування підмножини набору ", який описує алгоритм виконання частини того, що ви хочете - швидко генерувати всі підмножини з N символів довжиною x до y. Він містить реалізацію в C.
Для кожного підмножини вам все одно доведеться генерувати всі перестановки. Наприклад, якщо ви хотіли 3 символи з "abcde", цей алгоритм дасть вам "abc", "abd", "abe" ... але вам доведеться перестановити кожного, щоб отримати "acb", "bac", "бка" тощо.
Деякі діючі коди Java, засновані на відповіді Сарпа :
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);
}
}
Ось просте рішення в 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;
}
}
Тут є багато хороших відповідей. Я також пропоную дуже просте рекурсивне рішення в 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;
});
}
Примітка : рядки з повторними символами не створюють унікальних перестановок.
Я просто швидко пробив це в Рубі:
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, і поверніть результат.
Я не перевіряв його з потенційно безглуздим введенням (нульовий список символів, дивні значення x і y тощо).
Це переклад версії 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)))))
Ось просте рекурсивне рішення 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);
... і ось версія С:
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;
}
перестановка (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;
}
Друкує всі перестановки без дублікатів
Рекурсивний розчин у С ++
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;
}
}
}
У Perl, якщо ви хочете обмежитися лише маленьким алфавітом, ви можете зробити це:
my @result = ("a" .. "zzzz");
Це дає всі можливі рядки від 1 до 4 символів з використанням малих символів. Для верхнього регістру, зміни "a"
в "A"
і "zzzz"
до "ZZZZ"
.
У змішаному випадку це стає набагато складніше, і, мабуть, не піддається одному з таких вбудованих операторів Perl.
Відповідь Ruby, яка працює:
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")
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;
}
}
Наступна рекурсія 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()));
}
}
Я не впевнений, чому б ви хотіли це зробити в першу чергу. Отриманий набір для будь-яких помірно великих значень x і y буде величезним і зростатиме експоненціально, коли x та / або y стають більшими.
Скажімо, ваш набір можливих символів - це 26 малих літер алфавіту, і ви попросите свою програму генерувати всі перестановки, де довжина = 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);
}
}
}
}
Ось нерекурсивна версія, яку я придумав, у JavaScript. Він не заснований на нерекурсивному рекурсиві Кнута вище, хоча він має деякі подібності в заміні елементів. Я перевірив її правильність для введення масивів до 8 елементів.
Швидка оптимізація - це попередньо пролетіти out
масив та уникнути його push()
.
Основна ідея:
Давши єдиний масив джерел, генеруйте перший новий набір масивів, який обмінюється першим елементом з кожним наступним елементом по черзі, щоразу залишаючи інші елементи непорушеними. наприклад: задано 1234, генерують 1234, 2134, 3214, 4231.
Використовуйте кожен масив з попереднього проходу як початковий елемент для нового проходу, але замість того, щоб міняти перший елемент, поміняйте другий елемент на кожен наступний елемент. Також цього разу не включайте оригінальний масив у висновок.
Повторіть крок 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;
}
У рубіні:
str = "a"
100_000_000.times {puts str.next!}
Це досить швидко, але це займе певний час =). Звичайно, ви можете почати з "aaaaaaaa", якщо короткі рядки вам не цікаві.
Я, можливо, неправильно трактував власне запитання - в одному з дописів це звучало так, ніби тобі просто потрібна бібліотека брутальних сил струн, але в головному питанні це звучить так, що потрібно перестановити певний рядок.
Ваша проблема дещо схожа на цю: http://beust.com/weblog/archives/000491.html (перерахуйте всі цілі числа, в яких жодна з цифр не повторюється, що призвело до вирішення цілої кількості мов, ocaml хлопець, який використовує перестановки, і якийсь хлопець Java, який використовує ще одне рішення).
Мені це було потрібно сьогодні, і хоча вже дані відповіді вказували на мене в правильному напрямку, вони були не зовсім такими, якими я хотів.
Ось реалізація за допомогою методу Heap. Довжина масиву повинна бути не менше 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
Інші методи описані різними авторами. Кнут описує два, один наводить лексичний порядок, але складний і повільний, інший відомий як метод простих змін. Цзе Гао та Діянджун Ван також написали цікавий документ.
Цей код у python, коли його викликають із allowed_characters
встановленим значенням [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()
Сподіваюся, це вам корисно. Працює з будь-яким символом, не тільки цифрами
Ось посилання, що описує, як друкувати перестановки рядка. http://nipun-linuxtips.blogspot.in/2012/11/print-all-permutations-of-characters-in.html
Хоча це не відповідає точно на ваше запитання, ось один спосіб генерувати кожну перестановку літер з ряду рядків однакової довжини: наприклад, якщо ваші слова були "кава", "Джомла" та "Настрій", ви можете очікуйте випуску на кшталт "coodle", "joodee", "joffle" тощо.
В основному, кількість комбінацій - це (кількість слів) до потужності (кількість літер на слово). Отже, виберіть випадкове число між 0 і кількістю комбінацій - 1, перетворіть це число в базу (кількість слів), а потім використовуйте кожну цифру цього числа як показник, з якого слова взяти наступну букву.
наприклад: у наведеному вище прикладі. 3 слова, 6 літер = 729 сполучень. Виберіть випадкове число: 465. Перетворіть на базу 3: 122020. Візьміть першу букву із слова 1, 2-ю від слова 2, 3-ю від слова 2, 4-ю від слова 0 ... і отримаєте ... "Joofle".
Якби ви хотіли, щоб усі перестановки були просто циклом від 0 до 728. Звичайно, якщо ви просто вибираєте одне випадкове значення, набагато простішим менш заплутаним способом було б переведення циклу на літери. Цей метод дозволяє уникнути рекурсії, якщо ви хочете, щоб усі перестановки, а також він виглядав так, як ви знаєте математику (тм) !
Якщо кількість комбінацій є надмірною, ви можете розбити її на ряд менших слів і об'єднати їх наприкінці.
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;
}
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
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
Ось мій погляд на не рекурсивну версію
Пітонічний розчин:
from itertools import permutations
s = 'ABCDEF'
p = [''.join(x) for x in permutations(s)]
Ну ось вишукане, нерекурсивне, 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;
}