Utente:Grasso Luigi/sandbox4/Permutazioni: differenze tra le versioni

Contenuto cancellato Contenuto aggiunto
Grasso Luigi (discussione | contributi)
Grasso Luigi (discussione | contributi)
Riga 866:
 
===Algoritmi che generano permutazioni===
Nell'informatica può essere necessario generare permutazioni di una determinata successione di valori. I metodi più adatti a questo scopo dipendono dal fatto se si vogliano alcune permutazioni scelte casualmente o tutte le permutazioni e, in quest'ultimo caso, se è richiesto un ordinamento specifico. Un'altra questione è se si debba tener conto della possibile uguaglianza tra gli elementi della successione data; in tal caso, si dovrebbero generare solo permutazioni multiinsieme distinte della successione.
In computing it may be required to generate permutations of a given sequence of values. The methods best adapted to do this depend on whether one wants some randomly chosen permutations, or all permutations, and in the latter case if a specific ordering is required. Another question is whether possible equality among entries in the given sequence is to be taken into account; if so, one should only generate distinct multiset permutations of the sequence.
 
An obvious way to generate permutations of ''n'' is to generate values for the [[Lehmer code]] (possibly using the [[factorial number system]] representation of integers up to ''n''!), and convert those into the corresponding permutations. However, the latter step, while straightforward, is hard to implement efficiently, because it requires ''n'' operations each of selection from a sequence and deletion from it, at an arbitrary position; of the obvious representations of the sequence as an [[array data structure|array]] or a [[linked list]], both require (for different reasons) about ''n''<sup>2</sup>/4 operations to perform the conversion. With ''n'' likely to be rather small (especially if generation of all permutations is needed) that is not too much of a problem, but it turns out that both for random and for systematic generation there are simple alternatives that do considerably better. For this reason it does not seem useful, although certainly possible, to employ a special data structure that would allow performing the conversion from Lehmer code to permutation in [[big O notation|''O''(''n'' log ''n'')]] time.
Riga 879:
| pages = 26–27 | publisher = Oliver & Boyd | location = London | oclc = 14222135 }}</ref> While at the time computer implementation was not an issue, this method suffers from the difficulty sketched above to convert from Lehmer code to permutation efficiently. This can be remedied by using a different bijective correspondence: after using ''d''<sub>''i''</sub> to select an element among ''i'' remaining elements of the sequence (for decreasing values of ''i''), rather than removing the element and compacting the sequence by shifting down further elements one place, one [[swap (computer science)|swaps]] the element with the final remaining element. Thus the elements remaining for selection form a consecutive range at each point in time, even though they may not occur in the same order as they did in the original sequence. The mapping from sequence of integers to permutations is somewhat complicated, but it can be seen to produce each permutation in exactly one way, by an immediate [[induction (mathematics)|induction]]. When the selected element happens to be the final remaining element, the swap operation can be omitted. This does not occur sufficiently often to warrant testing for the condition, but the final element must be included among the candidates of the selection, to guarantee that all permutations can be generated.
 
TheL'algoritmo resultingrisultante algorithmper forgenerare generatinguna apermutazione randomcasuale permutation ofdi <code>''a''[0], ''a''[1], ..., ''a''[''n'' − 1]</code> can bepossiamo describeddescriverla ascon followsil inseguente [[pseudocodepseudocodice]]:
 
'''for''' ''i'' '''from''' ''n'' '''downto''' 2 '''do'''
Riga 892:
''a''[''d''<sub>''i''+1</sub>] ← ''i''
 
Ifse ''d''<sub>''i''+1</sub> = ''i'', thela firstprima assignmentassegnazione willcopierà copyun anvalore uninitializednon valueinizializzato, butma thela secondseconda willlo overwritesovrascriverà itcon withil thevalore correct valuecorretto ''i''.
 
HoweverTuttavia, l'algoritmo di Fisher-Yates isnon notè thequello fastestpiù algorithmveloce forper generatinggenerare auna permutationpermutazione, because Fisher-Yates is essentially a sequential algorithm and "divide and conquer" procedures can achieve the same result in parallel.<ref>{{cite news|author1=Bacher, A. |author2=Bodini, O.|author3=Hwang, H.K.|author4=Tsai, T.H. | title = Generating Random Permutations by Coin Tossing: Classical Algorithms, New Analysis, and Modern Implementation.
| edition = ACM Trans. Algorithms 13(2): 24:1–24:43| year = 2017| pages = 24–43}}</ref>
| year = 2017
| pages = 24–43
}}</ref>
 
====Generazione in ordine lessicografico====
There are many ways to systematically generate all permutations of a given sequence.<ref name=sedegewick1977>{{cite journal|last=Sedgewick|first=R|title=Permutation generation methods|journal=Computing Surveys|year=1977|volume=9
|issue=2|pages=137–164|url=http://www.math.uiowa.edu/~goodman/22m150.dir/2007/Permutation%20Generation%20Methods.pdf |archive-url=https://web.archive.org/web/20080221185652/http://www.math.uiowa.edu/~goodman/22m150.dir/2007/Permutation%20Generation%20Methods.pdf |archive-date=2008-02-21 |url-status=live|doi=10.1145/356689.356692|s2cid=12139332}}</ref>
|last=Sedgewick|first=R
|title=Permutation generation methods
|journal=Computing Surveys|year=1977|volume=9
|issue=2
|pages=137–164
|url=http://www.math.uiowa.edu/~goodman/22m150.dir/2007/Permutation%20Generation%20Methods.pdf |archive-url=https://web.archive.org/web/20080221185652/http://www.math.uiowa.edu/~goodman/22m150.dir/2007/Permutation%20Generation%20Methods.pdf |archive-date=2008-02-21 |url-status=live
|doi=10.1145/356689.356692
|s2cid=12139332
}}</ref>
One classic, simple, and flexible algorithm is based upon finding the next permutation in [[lexicographic ordering]], if it exists. It can handle repeated values, for which case it generates each distinct multiset permutation once. Even for ordinary permutations it is significantly more efficient than generating values for the Lehmer code in lexicographic order (possibly using the [[factorial number system]]) and converting those to permutations. It begins by sorting the sequence in (weakly) [[increasing]] order (which gives its lexicographically minimal permutation), and then repeats advancing to the next permutation as long as one is found. The method goes back to [[Narayana Pandit]]a in 14th century India, and has been rediscovered frequently.{{sfn|Knuth|2005|pp=1–26}}
 
Line 926 ⟶ 915:
Following this algorithm, the next lexicographic permutation will be [1, 3, 2, 4], and the 24th permutation will be [4, 3, 2, 1] at which point ''a''[''k''] < ''a''[''k'' + 1] does not exist, indicating that this is the last permutation.
 
This method uses about 3 comparisons and 1.5 swaps per permutation, amortized over the whole sequence, not counting the initial sort.<ref>{{cite web|title=std::next_permutation |url=http://en.cppreference.com/w/cpp/algorithm/next_permutation |access-date=31 March 2018 |work=cppreference.com|date=4 December 2017}}</ref>
 
==== Generazione con cambi minimi ====
{{main|Steinhaus–Johnson–Trotter algorithm|Heap's algorithm}}
An alternative to the above algorithm, the [[Steinhaus–Johnson–Trotter algorithm]], generates an ordering on all the permutations of a given sequence with the property that any two consecutive permutations in its output differ by swapping two adjacent values. This ordering on the permutations was known to 17th-century English bell ringers, among whom it was known as "plain changes". One advantage of this method is that the small amount of change from one permutation to the next allows the method to be implemented in constant time per permutation. The same can also easily generate the subset of even permutations, again in constant time per permutation, by skipping every other output permutation.{{sfn|Knuth|2005|pp=1–26}}
Line 935 ⟶ 924:
 
The following figure shows the output of all three aforementioned algorithms for generating all permutations of length <math>n=4</math>, and of six additional algorithms described in the literature.
[[File:Permutation generation algorithms10.svg|thumb|center|upright=2.2|Ordering of all permutations of length <math>n=4</math> generated by different algorithms. The permutations are color-coded, where {{legend-inline|red|1}}, {{legend-inline|yellow|2}}, {{legend-inline|green|3}}, {{legend-inline|blue|4}}.<ref>{{cite web|url=http://combos.org/perm|title=Generate permutations |last1=Mütze|first1=Torsten|last2=Sawada|first2=Joe |last3=Williams|first3=Aaron|website=Combinatorial Object Server|access-date=May 29, 2019}}</ref>]]
 
# Ordinamento lessicografico;
# Lexicographic ordering;
# [[Algoritmo di Steinhaus–Johnson–Trotter]];
# [[Steinhaus–Johnson–Trotter algorithm]];
# [[Heap'sAlgoritmo algorithmdell'Heap]];
# Ehrlich's star-transposition algorithm:{{sfn|Knuth|2005|pp=1–26}} in each step, the first entry of the permutation is exchanged with a later entry;
# Zaks' prefix reversal algorithm:<ref name="Zaks_1984">{{cite journal|last=Zaks|first=S.|title=A new algorithm for generation of permutations|journal=[[BIT Numerical Mathematics]]|year=1984|volume=24|issue=2|pages=196–204|doi=10.1007/BF01937486|s2cid=30234652}}</ref> in each step, a prefix of the current permutation is reversed to obtain the next permutation;
Line 948 ⟶ 937:
# Nested swaps generating algorithm in steps connected to the nested subgroups <math>S_k\subset S_{k+1}</math>. Each permutation is obtained from the previous by a transposition multiplication to the left. Algorithm is connected to the [[Factorial_number_system]] of the index.
 
==== Generazione di permutazioni in passaggi di scambi nidificati ====
====Generation of permutations in nested swap steps====
Explicit sequence of swaps (transpositions, 2-cycles <math>(pq)</math>), is described here, each swap applied (on the left) to the previous chain providing a new permutation, such that all the permutations can be retrieved, each only once.<ref>{{cite book|author1=Popp, O.T. | title = Quickly Handling Big Permutations| orig-year = | edition =| year = 2002| pages=| publisher = priv. comm.| location = | oclc = }}</ref> This counting/generating procedure has an additional structure (call it nested), as it is given in steps: after completely retrieving <math>S_{k-1}</math>, continue retrieving <math>S_{k}\backslash S_{k-1}</math> by cosets <math>S_{k-1}\tau_i</math> of <math>S_{k-1}</math> in <math>S_k</math>, by appropriately choosing the coset representatives <math>\tau_i</math> to be described below. Note that, since each <math>S_m</math> is sequentially generated, there is a ''last element'' <math>\lambda_m\in S_m</math>. So, after generating <math>S_{k-1}</math> by swaps, the next permutation in <math>S_{k}\backslash S_{k-1}</math> has to be <math>\tau_1=(p_1k)\lambda_{k-1}</math> for some <math>1\leq p_1<k</math>. Then all swaps that generated <math>S_{k-1}</math> are repeated, generating the whole coset <math>S_{k-1}\tau_1</math>, reaching the last permutation in that coset <math>\lambda_{k-1}\tau_1</math>; the next swap has to move the permutation to representative of another coset <math>\tau_2=(p_2k)\lambda_{k-1}\tau_1</math>.
Explicit sequence of swaps (transpositions, 2-cycles <math>(pq)</math>), is described here, each swap applied (on the left) to the previous chain providing a new permutation, such that all the permutations can be retrieved, each only once.<ref>{{cite book
|author1=Popp, O.T. | title = Quickly Handling Big Permutations
| orig-year =
| edition =
| year = 2002
| pages =
| publisher = priv. comm.
| location =
| oclc =
}}</ref> This counting/generating procedure has an additional structure (call it nested), as it is given in steps: after completely retrieving <math>S_{k-1}</math>, continue retrieving <math>S_{k}\backslash S_{k-1}</math> by cosets <math>S_{k-1}\tau_i</math> of <math>S_{k-1}</math> in <math>S_k</math>, by appropriately choosing the coset representatives <math>\tau_i</math> to be described below. Note that, since each <math>S_m</math> is sequentially generated, there is a ''last element'' <math>\lambda_m\in S_m</math>. So, after generating <math>S_{k-1}</math> by swaps, the next permutation in <math>S_{k}\backslash S_{k-1}</math> has to be <math>\tau_1=(p_1k)\lambda_{k-1}</math> for some <math>1\leq p_1<k</math>. Then all swaps that generated <math>S_{k-1}</math> are repeated, generating the whole coset <math>S_{k-1}\tau_1</math>, reaching the last permutation in that coset <math>\lambda_{k-1}\tau_1</math>; the next swap has to move the permutation to representative of another coset <math>\tau_2=(p_2k)\lambda_{k-1}\tau_1</math>.
 
Continuing the same way, one gets coset representatives <math>\tau_j=(p_{j}k)\lambda_{k-1}\cdots \lambda_{k-1}(p_{i}k)\lambda_{k-1}\cdots\lambda_{k-1}(p_{1}k)\lambda_{k-1}</math> for the cosets of <math>S_{k-1}</math> in <math>S_k</math>; the ordered set <math>(p_1,\ldots , p_{k-1})</math> (<math>0\leq p_i<k</math>) is called the set of coset beginnings. Two of these representatives are in the same coset if and only if <math>\tau_j(\tau_i)^{-1}=(p_{j}k)\lambda_{k-1}(p_{j-1}k)\lambda_{k-1}\cdots \lambda_{k-1}(p_{i+1}k)=\varkappa_{ij}\in S_{k-1}</math>, that is, <math>\varkappa_{ij} (k)=k</math>. Concluding, permutations <math>\tau_i\in S_k-S_{k-1}</math> are all representatives of distinct cosets if and only if for any <math>k>j>i\geq 1</math>, <math>(\lambda_{k-1})^{j-i}p_{i}\neq p_j</math> (no repeat condition). In particular, for all generated permutations to be distinct it is not necessary for the <math>p_i</math> values to be distinct. In the process, one gets that <math>\lambda_k=\lambda_{k-1}(p_{k-1}k)\lambda_{k-1}(p_{k-2}k)\lambda_{k-1}\cdots\lambda_{k-1}(p_{1}k)\lambda_{k-1}</math> and this provides the recursion procedure.
 
EXAMPLESESEMPI: obviously, for <math>\lambda_2</math> one has <math>\lambda_2=(12)</math>; to build <math>\lambda_3</math> there are only two possibilities for the coset beginnings satisfying the no repeat condition; the choice <math> p_1=p_2=1</math> leads to <math>\lambda_3=\lambda_2(13)\lambda_2(13)\lambda_2=(13)</math>. To continue generating <math>S_4</math> one needs appropriate coset beginnings (satisfying the no repeat condition): there is a convenient choice: <math>p_1=1, p_2=2, p_3=3</math>, leading to <math>\lambda_4=(13)(1234)(13)=(1432)</math>. Then, to build <math>\lambda_5</math> a convenient choice for the coset beginnings (satisfying the no repeat condition) is <math> p_1=p_2=p_3=p_4=1</math>, leading to <math>\lambda_5=(15)</math>.
 
From examples above one can inductively go to higher <math>k</math> in a similar way, choosing coset beginnings of <math>S_{k}</math> in <math>S_{k+1}</math>, as follows: for <math>k</math> even choosing all coset beginnings equal to 1 and for <math>k</math> odd choosing coset beginnings equal to <math>(1, 2,\dots , k)</math>. With such choices the "last" permutation is <math>\lambda_k=(1k)</math> for <math>k</math> odd and <math>\lambda_k=(1k_-)(12\cdots k)(1k_-)</math> for <math>k</math> even (<math>k_-=k-1</math>). Using these explicit formulae one can easily compute the permutation of certain index in the counting/generation steps with minimum computation. For this, writing the index in factorial base is useful. For example, the permutation for index <math>699=5(5!)+4(4!)+1(2!)+1(1!)</math> is: <math>\sigma=\lambda_2(13)\lambda_2(15)\lambda_4(15)\lambda_4(15)\lambda_4(15)\lambda_4(56)\lambda_5(46)\lambda_5(36)\lambda_5(26)\lambda_5(16)\lambda_5=</math> <math>\lambda_2(13)\lambda_2((15)\lambda_4)^4(\lambda_5)^{-1}\lambda_6=(23)(14325)^{-1}(15)(15)(123456)(15)=</math><math>(23)(15234)(123456)(15)</math>, yelding finally, <math>\sigma=(1653)(24)</math>.