Python中的排列組合
itertools
Python 提供了直接的方法來查找序列的排列和組合。這些方法存在於 itertools 包中。
排列
首先導入itertools包,在python中實現permutations方法。此方法將列表作為輸入並返回包含列表形式的所有排列的元組對象列表。
# A Python program to print all
# permutations using library function
from itertools import permutations
# Get all permutations of [1, 2, 3]
perm = permutations([1, 2, 3])
# Print the obtained permutations
for i in list(perm):
print (i)
輸出:
(1, 2, 3)
(1, 3, 2)
(2, 1, 3)
(2, 3, 1)
(3, 1, 2)
(3, 2, 1)
它生成 n! 如果輸入序列的長度為 n,則排列。
如果想要得到長度為 L 的排列,那么以這種方式實現它。
# A Python program to print all
# permutations of given length
from itertools import permutations
# Get all permutations of length 2
# and length 2
perm = permutations([1, 2, 3], 2)
# Print the obtained permutations
for i in list(perm):
print (i)
輸出:
(1, 2)
(1, 3)
(2, 1)
(2, 3)
(3, 1)
(3, 2)
它生成 nCr * r! 如果輸入序列的長度為 n 且輸入參數為 r,則排列。
組合
此方法將一個列表和一個輸入 r 作為輸入,並返回一個元組對象列表,其中包含列表形式的長度 r 的所有可能組合。
# A Python program to print all
# combinations of given length
from itertools import combinations
# Get all combinations of [1, 2, 3]
# and length 2
comb = combinations([1, 2, 3], 2)
# Print the obtained combinations
for i in list(comb):
print (i)
輸出:
(1, 2)
(1, 3)
(2, 3)
- 組合按輸入的字典排序順序發出。因此,如果輸入列表已排序,則組合元組將按排序順序生成。
# A Python program to print all
# combinations of a given length
from itertools import combinations
# Get all combinations of [1, 2, 3]
# and length 2
comb = combinations([1, 2, 3], 2)
# Print the obtained combinations
for i in list(comb):
print (i)
輸出:
(1, 2)
(1, 3)
(2, 3)
- 元素根據它們的位置而不是它們的價值被視為唯一的。因此,如果輸入元素是唯一的,則每個組合中都不會出現重復值。
# A Python program to print all combinations
# of given length with unsorted input.
from itertools import combinations
# Get all combinations of [2, 1, 3]
# and length 2
comb = combinations([2, 1, 3], 2)
# Print the obtained combinations
for i in list(comb):
print (i)
輸出:
(2, 1)
(2, 3)
(1, 3)
3.如果我們想將相同的元素組合成相同的元素,那么我們使用combinations_with_replacement。
# A Python program to print all combinations
# with an element-to-itself combination is
# also included
from itertools import combinations_with_replacement
# Get all combinations of [1, 2, 3] and length 2
comb = combinations_with_replacement([1, 2, 3], 2)
# Print the obtained combinations
for i in list(comb):
print (i)
輸出:
(1, 1)
(1, 2)
(1, 3)
(2, 2)
(2, 3)
(3, 3)
打印給定字符串的所有排列
排列也稱為“排列編號”或“順序”,是將有序列表 S 的元素重新排列為與 S 本身一一對應的排列。長度為 n 的字符串有 n! 排列。
下面是字符串 ABC 的排列。
ABC ACB BAC BCA CBA CAB

# Python program to print all permutations with
# duplicates allowed
def toString(List):
return ''.join(List)
# Function to print permutations of string
# This function takes three parameters:
# 1. String
# 2. Starting index of the string
# 3. Ending index of the string.
def permute(a, l, r):
if l==r:
print toString(a)
else:
for i in xrange(l,r+1):
a[l], a[i] = a[i], a[l]
permute(a, l+1, r)
a[l], a[i] = a[i], a[l] # backtrack
# Driver program to test the above function
string = "ABC"
n = len(string)
a = list(string)
permute(a, 0, n-1)
# This code is contributed by Bhavya Jain
Output:
ABC
ACB
BAC
BCA
CBA
CAB
算法范式:回溯
時間復雜度: O(n*n!) 注意有 n! 排列,打印排列需要 O(n) 時間。
輔助空間: O(r – l)
注意:如果輸入字符串中有重復字符,上述解決方案會打印重復排列。
另一種方法:
def permute(s, answer):
if (len(s) == 0):
print(answer, end = " ")
return
for i in range(len(s)):
ch = s[i]
left_substr = s[0:i]
right_substr = s[i + 1:]
rest = left_substr + right_substr
permute(rest, answer + ch)
# Driver Code
answer = ""
s = input("Enter the string : ")
print("All possible strings are : ")
permute(s, answer)
# This code is contributed by Harshit Srivastava
輸出:
輸入字符串:abc
所有可能的字符串是:abc acb bac bca cab cba
時間復雜度: O(n*n!) 時間復雜度和上面的方法一樣,即有n!排列,打印排列需要 O(n) 時間。
輔助空間: O(|s|)
打印具有重復項的給定字符串的所有不同排列
給定一個可能包含重復的字符串,編寫一個函數來打印給定字符串的所有排列,以便在輸出中沒有重復排列。
例子:
輸入:str[] = "AB"
輸出:AB BA
輸入:str[] = "AA"
輸出:AA
輸入:str[] = "ABC"
輸出:ABC ACB BAC BCA CBA CAB
輸入:str[] = "ABA"
輸出:ABA AAB BAA
輸入:str[] = "ABCA"
輸出:AABC AACB ABAC ABCA ACBA ACAB BAAC BACA
BCAA CABA CAAB CBAA
// Program to print all permutations of a
// string in sorted order.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Following function is needed for library
function qsort(). */
int compare(const void* a, const void* b)
{
return (*(char*)a - *(char*)b);
}
// A utility function two swap two characters
// a and b
void swap(char* a, char* b)
{
char t = *a;
*a = *b;
*b = t;
}
// This function finds the index of the
// smallest character which is greater
// than 'first' and is present in str[l..h]
int findCeil(char str[], char first, int l, int h)
{
// initialize index of ceiling element
int ceilIndex = l;
// Now iterate through rest of the
// elements and find the smallest
// character greater than 'first'
for (int i = l + 1; i <= h; i++)
if (str[i] > first && str[i] < str[ceilIndex])
ceilIndex = i;
return ceilIndex;
}
// Print all permutations of str in sorted order
void sortedPermutations(char str[])
{
// Get size of string
int size = strlen(str);
// Sort the string in increasing order
qsort(str, size, sizeof(str[0]), compare);
// Print permutations one by one
bool isFinished = false;
while (!isFinished) {
// print this permutation
static int x = 1;
printf("%d %s \n", x++, str);
// Find the rightmost character
// which is smaller than its next
// character. Let us call it 'first
// char'
int i;
for (i = size - 2; i >= 0; --i)
if (str[i] < str[i + 1])
break;
// If there is no such character, all
// are sorted in decreasing order,
// means we just printed the last
// permutation and we are done.
if (i == -1)
isFinished = true;
else {
// Find the ceil of 'first char'
// in right of first character.
// Ceil of a character is the
// smallest character greater
// than it
int ceilIndex = findCeil(str,
str[i], i + 1, size - 1);
// Swap first and second characters
swap(&str[i], &str[ceilIndex]);
// Sort the string on right of 'first char'
qsort(str + i + 1, size - i - 1,
sizeof(str[0]), compare);
}
}
}
// Driver program to test above function
int main()
{
char str[] = "ACBC";
sortedPermutations(str);
return 0;
}
輸出
1 ABCC
2 ACBC
3 ACCB
4 BACC
5 BCAC
6 BCCA
7 CABC
8 CACB
9 CBAC
10 CBCA
11 CCAB
12 CCBA
時間復雜度:O(n 2 * n!)
輔助空間:O(1)
上述算法的時間復雜度為 O(n2 * n!) 但我們可以通過一些修改實現 O(n!*n) 的更好時間復雜度,這是在輸入中所有不同字符的情況下存在的算法。
有效的方法:在我們的遞歸函數中找到所有排列,我們可以使用 unordered_set 來處理活動字符串中剩余的重復元素。在迭代字符串的元素時,我們將在 unordered_set 中檢查該元素,如果找到,則我們將跳過該迭代,否則我們將將該元素插入到 unordered_set 中。平均而言,所有像 insert() 和 find() 的 unordered_set 操作都在 O(1) 時間內,因此使用 unordered_set 不會改變算法時間復雜度。
#include <algorithm>
#include <iostream>
#include <unordered_set>
using namespace std;
void printAllPermutationsFast(string s, string l)
{
if (s.length() < 1) {
cout << l + s << endl;
}
unordered_set<char> uset;
for (int i = 0; i < s.length(); i++) {
if (uset.find(s[i]) != uset.end()) {
continue;
}
else {
uset.insert(s[i]);
}
string temp = "";
if (i < s.length() - 1) {
temp = s.substr(0, i) + s.substr(i + 1);
}
else {
temp = s.substr(0, i);
}
printAllPermutationsFast(temp, l + s[i]);
}
}
int main()
{
string s = "ACBC";
sort(s.begin(), s.end());
printAllPermutationsFast(s, "");
return 0;
}
Output
ABCC
ACBC
ACCB
BACC
BCAC
BCCA
CABC
CACB
CBAC
CBCA
CCAB
CCBA
時間復雜度 - O(n*n!)
輔助空間 - O(n)
總和等於 X 的子集計數
給定一個長度為N的數組arr[]和一個整數X,任務是找到總和等於X的子集的數量。
例子:
Input: arr[] = {1, 2, 3, 3}, X = 6
Output: 3
All the possible subsets are {1, 2, 3},
{1, 2, 3} and {3, 3}
Input: arr[] = {1, 1, 1, 1}, X = 1
Output: 4
方法一:
一個簡單的方法是通過生成所有可能的子集,然后檢查該子集是否具有所需的總和來解決此問題。這種方法將具有指數時間復雜度。但是,對於較小的X值和數組元素,可以使用動態規划解決此問題。
我們先來看遞歸關系。
此方法對所有整數都有效。
dp[i][C] = dp[i – 1][C – arr[i]] + dp[i – 1][C]
現在讓我們了解 DP 的狀態。這里,dp[i][C]存儲子數組arr[i…N-1]的子集數量,使得它們的總和等於C。
因此,遞歸是非常微不足道的,因為只有兩種選擇,即要么考慮子集中的第i個元素,要么不考慮。
# Python3 implementation of the approach
import numpy as np
maxN = 20
maxSum = 50
minSum = 50
base = 50
# To store the states of DP
dp = np.zeros((maxN, maxSum + minSum));
v = np.zeros((maxN, maxSum + minSum));
# Function to return the required count
def findCnt(arr, i, required_sum, n) :
# Base case
if (i == n) :
if (required_sum == 0) :
return 1;
else :
return 0;
# If the state has been solved before
# return the value of the state
if (v[i][required_sum + base]) :
return dp[i][required_sum + base];
# Setting the state as solved
v[i][required_sum + base] = 1;
# Recurrence relation
dp[i][required_sum + base] = findCnt(arr, i + 1,
required_sum, n) + \
findCnt(arr, i + 1,
required_sum - arr[i], n);
return dp[i][required_sum + base];
# Driver code
if __name__ == "__main__" :
arr = [ 3, 3, 3, 3 ];
n = len(arr);
x = 6;
print(findCnt(arr, 0, x, n));
# This code is contributed by AnkitRai01
輸出
6
方法二:使用制表法:
This method is valid only for those arrays which contains positive elements.
In this method we use a 2D array of size (arr.size() + 1) * (target + 1) of type integer.
Initialization of Matrix:
mat[0][0] = 1 because If the size of sum is
if (A[i] > j)
DP[i][j] = DP[i-1][j]
else
DP[i][j] = DP[i-1][j] + DP[i-1][j-A[i]]
這意味着如果當前元素的值大於“當前總和值”,我們將復制之前案例的答案
如果當前的總和值大於 'ith' 元素,我們將看到是否有任何先前的狀態已經經歷過 sum='j' 並且任何先前的狀態經歷了一個值 'j – A[i]' 這將是我們的目的
def subset_sum(a: list, n: int, sum: int):
# Initializing the matrix
tab = [[0] * (sum + 1) for i in range(n + 1)]
for i in range(1, sum + 1):
tab[0][i] = 0
for i in range(n+1):
# Initializing the first value of matrix
tab[i][0] = 1
for i in range(1, n+1):
for j in range(1, sum + 1):
if a[i-1] <= j:
tab[i][j] = tab[i-1][j] + tab[i-1][j-a[i-1]]
else:
tab[i][j] = tab[i-1][j]
return tab[n][sum]
if __name__ == '__main__':
a = [3, 3, 3, 3]
n = 4
sum = 6
print(subset_sum(a, n, sum))
# This code is contributed by Premansh2001.
輸出
6
時間復雜度: O(sum*n),其中總和是“目標總和”,“n”是數組的大小。
輔助空間: O(sumn),作為二維數組的大小,是sumn。
計算 nCr 值的程序
二項式系數的常見定義:
- A binomial coefficient C(n, k) can be defined as the coefficient of X^k in the expansion of (1 + X)^n.
- A binomial coefficient C(n, k) also gives the number of ways, disregarding order, that k objects can be chosen from among n objects; more formally, the number of k-element subsets (or k-combinations) of an n-element set.
Given two numbers n and r, find value of nCr
nCr = (n!) / (r! * (n-r)!)
示例:
Input : n = 5, r = 2
Output : 10
The value of 5C2 is 10
Input : n = 3, r = 1
Output : 3
# Python 3 program To calculate
# The Value Of nCr
def nCr(n, r):
return (fact(n) / (fact(r)
* fact(n - r)))
# Returns factorial of n
def fact(n):
res = 1
for i in range(2, n+1):
res = res * i
return res
# Driver code
n = 5
r = 3
print(int(nCr(n, r)))
# This code is contributed
# by Smitha
Output:
10
大數階乘
非負整數的階乘,是所有小於或等於 n 的整數的乘積。例如 6 的階乘是 6*5*4*3*2*1,即 720。
Examples :
Input : 100
Output : 933262154439441526816992388562667004-
907159682643816214685929638952175999-
932299156089414639761565182862536979-
208272237582511852109168640000000000-
00000000000000
Input :50
Output : 3041409320171337804361260816606476884-
4377641568960512000000000000
方法一: 我們使用數組來存儲結果的各個數字。
下面是求階乘的詳細算法:
factorial(n)
- 創建一個最大大小的數組“res[]”,其中 MAX 是輸出中的最大位數。
- 將存儲在 'res[]' 中的值初始化為 1,並將 'res_size' ('res[]' 的大小) 初始化為 1。
- 對從 x = 2 到 n 的所有數字執行以下操作。
……a) 將 x 與 res[] 相乘並更新 res[] 和 res_size 以存儲乘法結果。
如何將數字“x”與存儲在 res[] 中的數字相乘?
我們將 x 與 res[] 的每一位數字一一相乘。這里要注意的重點是數字從最右邊的數字到最左邊的數字相乘。如果我們在 res[] 中以相同的順序存儲數字,那么在沒有額外空間的情況下更新 res[] 變得困難。這就是為什么 res[] 以相反的方式維護,即從右到左存儲數字。
multiply(res[], x)
- 將進位初始化為 0。
- 對 i = 0 執行以下操作到 res_size – 1
....a) 找到 res[i] * x + 進位的值。讓這個價值成為現實。
....b) 通過在其中存儲 prod 的最后一位來更新 res[i]。
....c) 通過將剩余數字存儲在進位中來更新進位。 - 將進位的所有數字放入 res[] 並通過進位位數增加 res_size。
Example to show working of multiply(res[], x)
A number 5189 is stored in res[] as following.
res[] = {9, 8, 1, 5}
x = 10
Initialize carry = 0;
i = 0, prod = res[0]*x + carry = 9*10 + 0 = 90.
res[0] = 0, carry = 9
i = 1, prod = res[1]*x + carry = 8*10 + 9 = 89
res[1] = 9, carry = 8
i = 2, prod = res[2]*x + carry = 1*10 + 8 = 18
res[2] = 8, carry = 1
i = 3, prod = res[3]*x + carry = 5*10 + 1 = 51
res[3] = 1, carry = 5
res[4] = carry = 5
res[] = {0, 9, 8, 1, 5}
注意:在下面的實現中,輸出中的最大位數假定為 500。要找到更大數字(> 254)的階乘,請增加數組的大小或增加 MAX 的值。
# Python program to compute factorial
# of big numbers
import sys
# This function finds factorial of large
# numbers and prints them
def factorial( n) :
res = [None]*500
# Initialize result
res[0] = 1
res_size = 1
# Apply simple factorial formula
# n! = 1 * 2 * 3 * 4...*n
x = 2
while x <= n :
res_size = multiply(x, res, res_size)
x = x + 1
print ("Factorial of given number is")
i = res_size-1
while i >= 0 :
sys.stdout.write(str(res[i]))
sys.stdout.flush()
i = i - 1
# This function multiplies x with the number
# represented by res[]. res_size is size of res[]
# or number of digits in the number represented
# by res[]. This function uses simple school
# mathematics for multiplication. This function
# may value of res_size and returns the new value
# of res_size
def multiply(x, res,res_size) :
carry = 0 # Initialize carry
# One by one multiply n with individual
# digits of res[]
i = 0
while i < res_size :
prod = res[i] *x + carry
res[i] = prod % 10; # Store last digit of
# 'prod' in res[]
# make sure floor division is used
carry = prod//10; # Put rest in carry
i = i + 1
# Put carry in res and increase result size
while (carry) :
res[res_size] = carry % 10
# make sure floor division is used
# to avoid floating value
carry = carry // 10
res_size = res_size + 1
return res_size
# Driver program
factorial(100)
#This code is contributed by Nikita Tiwari.
輸出
定數的階乘是
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
方法二: 也可以使用鏈表,這種方式不會浪費任何額外的空間。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i = a; i <= b; i++)
using namespace std;
// Made a class node containing data and previous pointer as
// we are using tail pointer
class Node {
public:
int data;
Node* prev;
Node(int n)
{
data = n;
prev = NULL;
}
};
void Multiply(Node* tail, int n)
{
Node *temp = tail,
*prevNode = tail; // Temp variable for keeping tail
int carry = 0;
while (temp != NULL) {
int data = temp->data * n + carry;
temp->data = data % 10; // stores the last digit
carry = data / 10;
prevNode = temp;
temp = temp->prev; // Moving temp by 1 prevNode will
// now denote temp
}
// If carry is greater than 0 then we create another
// node for it.
while (carry != 0) {
prevNode->prev = new Node((int)(carry % 10));
carry /= 10;
prevNode = prevNode->prev;
}
}
void print(Node* tail)
{
if (tail == NULL) // Using tail recursion
return;
print(tail->prev);
cout
<< tail->data; // Print linked list in reverse order
}
// Driver code
int main()
{
int n = 20;
Node tail(1); // Create a node and initialise it by 1
rep(i, 2, n)
Multiply(&tail, i); // Run a loop from 2 to n and
// multiply with tail's i
print(&tail); // Print the linked list
cout << endl;
return 0;
}
// This code is contributed by Kingshuk Deb
輸出
2432902008176640000