Permutationen erzeugen

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
Froggerprogger
Badmin
Beiträge: 855
Registriert: 08.09.2004 20:02

Permutationen erzeugen

Beitrag von Froggerprogger »

Folgender Code erzeugt sämtliche Permutationen mit den Zahlen 1 bis p_numElems und ruft für jede Permutation die übergebene Funktion *p_callbackProc auf, welche mit den permutierten Zahlen dann alles mögliche anstellen kann. (siehe z.B. example 2 für Stringkonkatenation)

Dabei können beliebig viele Elemente permutiert werden, selbst wenn die Anzahl ihrer Permutationen die 2^31-Grenze überschreitet.

[Anm: Permutation = Aneinanderreihung von Elementen der Ausgangsmenge (hier der Zahlen 1..p_numElems), wobei jedes genau einmal und an einer beliebigen Position auftaucht]

Code: Alles auswählen

;/ PermutationCaller(p_numElems.l, *p_callbackProc.l)
;/ >> generates all permutations of numbers 1..p_numElems and calls *p_callbackProc on
;/ >> each permutation to use it for any purpose
;/ 
;/ p_numElems:       the number of elements to permutate (1..)
;/ *p_callbackProc:    pointer to function to call each time a new permutation
;/                   was generated. This function has to be of the form:
;/                   ProcName (*p_array.l, p_numElems.l, p_counter.l)
;/                   where [*p_array] will hold a pointer to an array
;/                   of [p_numElems] permutated long-values each in
;/                   range 1..[p_numElems]
;/                   p_counter just counts from 1..
;/                   This callbackfunction has to return 0 (or nothing) to let
;/                   PermutationCaller() continue,
;/                   or may return anything <> 0 to let PermutationCaller()
;/                   stop immediately.
;/
;/ by Froggerprogger, 20.11.2004
Procedure.l PermutationCaller(p_numElems.l, *p_callbackProc.l)
  Dim Permutation.l (p_numElems - 1) ; holds the actual permutation
  Dim TempFreedValues.l (p_numElems - 1) ; entry 0=used, 1=(temporarily) not used value
   
  Protected resume.l : resume = 1
  Protected runPos.l : runPos = p_numElems - 2
  Protected tempL.l
  Protected lastVal.l
  Protected counter.l
  
  ; nothing to permutate with p_numElems <= 1
  If p_numElems <= 1
    ProcedureReturn 0
  EndIf
  
  ; fill in the first permutation
  For i=0 To p_numElems - 1
    Permutation(i) = i+1 
  Next

  Repeat
    counter + 1
    ; call the custom function
    tempL = CallFunctionFast(*p_callbackProc, @Permutation(), p_numElems, counter)
    If tempL <> 0  
      ProcedureReturn tempL
    EndIf
    
    ; free all entries from runPos to end
    descending = 1
    lastVal = Permutation(runPos)
    TempFreedValues(lastVal - 1) = 1
    For i = runPos + 1 To p_numElems - 1
      tempL = Permutation(i)
      TempFreedValues(tempL - 1) = 1
      If descending And tempL > lastVal
        descending = 0
      ElseIf descending
        lastVal = tempL
      EndIf 
    Next

    While descending
      ; if at runPos the local maximum is, then free it and go one more left to increase it
      runPos - 1
      ; quit if this was the most left position
      If runPos < 0 : ProcedureReturn counter : EndIf
      tempL = Permutation(runPos)
      TempFreedValues(tempL-1) = 1
      lastVal = Permutation(runPos+1)
      If tempL < lastVal
        descending = 0
      EndIf
    Wend

    ; write nearest greater value to runPos
    i = Permutation(runPos)
    Repeat 
      i+1
    Until TempFreedValues(i-1) = 1
    TempFreedValues(i-1) = 0
    Permutation(runPos) = i
    
    ; write back the other freed values in descending order
    j = runPos + 1
    For i=0 To p_numElems - 1
      If TempFreedValues(i) = 1
        TempFreedValues(i) = 0
        Permutation(j) = i+1
        j+1
      EndIf
    Next
    runPos = p_numElems - 2
  ForEver
EndProcedure

;-
;- 2 Examples
;-
;/ example 1 - simple number permutation
#max1 = 4
Debug "example 1: simple number permutation"
Procedure SimpleDebugArray(*p_array.l, p_numElems.l, p_counter.l)
  ;( >>sniff<< there are no arrays as parameters in PB)
  Protected res.s
  res = ""
  For i=0 To p_numElems-1
    res + Str(PeekL(*p_array + 4*i)) + "  |  "
  Next
  Debug res
  ProcedureReturn 0
EndProcedure

Debug Str(PermutationCaller(#max1, @SimpleDebugArray())) + " permutations"
Debug "" : Debug ""

;/ example 2 - using permutationcaller for another purepose (strings' combination here)
#max2 = 5
Dim MyStringArray.s(#max2 - 1)

Debug "example 2: using permutationcaller() for strings' combination"
Procedure DebugStrings(*p_array.l, p_numElems.l, p_counter.l)
  ;( >>sniff<< there are no arrays as parameters in PB)
  Protected res.s
  res = ""
  For i=0 To p_numElems-1
    res + MyStringArray(PeekL(*p_array + 4*i)-1) + " "
  Next
  Debug res
  ProcedureReturn 0
EndProcedure

; fill some strings into an array

For i=0 To #max2-1
  MyStringArray(i) = Chr(65 + i) + Str(i) + Chr(97 + i)
Next

Debug Str(PermutationCaller(#max2, @DebugStrings())) + " permutations"
!UD2