1..24 umcodieren in Kombination von 4 Ziffern

Zuviel Werbung?
-> Hier kostenlos registrieren
Ich habe mir mal die Ergebniszahlen mit Excel angeschaut, ob da irgendeine berechenbare Regelmäßigkeit auffällt ... siehe Bild

Wenn man die Kombinationen sozusagen im Quaternärsystem plus 1 durchgeht, dann ergibt sich ein ähnliches Bild, aber wenn man als Endwert den Maximalwert mit 4 Ziffern in diesem System annimmt, dann spiegeln sich die Abstände an einem Wert.

Aber ob sich daraus ein Algorithmus basteln lässt, da ist mir auch noch nichts eingefallen.

1646772535477.png
 
Sooo, ich habe meinen VBA-Krempel noch überarbeitet:
- Eine Funktion, um aus einem gegebenen Index X1 die Permutation zu berechnen und
- eine weitere Funktion, um aus einer gegebenen Permutation X1 den Index zu berechnen.
Die Permutation ist ein String aus n=2..9 Zeichen ohne Wiederholung.
Die Zeichen dürfen Ziffern sein, müssen sie aber nicht. Möglich sind auch Buchstaben und SonderZeichen oder Kombinationen daraus.

Eine eindeutige Zuordnung zwischen einem Index und einer entsprechenden Permutation ist nur dann sinnvoll zu handhaben, wenn die Permutationen aufsteigend (wie dies in Haralds Aufgabenstellung der Fall ist s.u.) oder absteigend sortiert sind.

1 --> 1234
2 --> 1243
3 --> 1324
4 --> 1342
5 --> 1423
6 --> 1432
7 --> 2134
8 --> 2143
...
24 --> 4321

Um die verwendeten bzw. zu verwendenden Zeichen und ihre Wertung (für die Sortierung) der Funktion zu übergeben, habe ich den zweiten Parameter X2 hinzugefügt.
In Haralds Aufgabe, mit den 4 in der Permutation zu verwendenden Zeichen '1', '2', '3' und '4' sieht dieser zweite Parameter so aus "1234" bzw. ein FunktionsAufruf so
myPermutation = NummiX( myIndex, "1234")
oder für die umgekehrte Richtung (Permutation --> Index) so
myIndex = IdxNummiX(myPermutation, "1234")
Anstelle von "1234" darf natürlich eine StringVariable angegeben werden, die den Inhalt "1234" hat.

Die Funktionen prüfen u.a. die Länge dieses Parameters. Sie darf minimal 2 und maximal 9 betragen.
U.a. wird bei IdxNummiX auch geprüft, ob die Strings X1 (die Permutation) und X2 (die verwendeten / zu verwendenden Zeichen) dieselben Zeichen enthalten, wobei in X1 ihre Reihenfolge keine Rolle spielt.

Die Reihenfolge der Zeichen in X2 hat aber eine Bedeutung. Sie gibt an, wie die Zeichen gewertet und somit sie und ihre Permutationen sortiert werden.
Die Zeichen in X2 müssen von links nach rechts entsprechend ihrer beabsichtigten Wertung aufsteigend eingegeben werden.
Ändert man z.B. das obige Beispiel in
myIndex = NummiX(myPermutation, "4321")
so wird der Index die Permutationen in scheinbar umgekehrter Sortierung liefern:
1 --> "4321"
2 --> "4312"
u.s.w.
23 --> "1243"
24 --> "1234"

Denkbar wären z.B. auch folgende Angaben:
myIndex = NummiX(myPermutation, "13579") ' // Index: 1..120
myIndex = NummiX(myPermutation, "abcd") ' // Index: 1..24
myIndex = NummiX(myPermutation, "aeiou") ' // Index: 1..120
myIndex = NummiX(myPermutation, "<=>") ' // Index: 1..6

Code:
' - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - +
Function NummiX(X1, X2)      ' // Index --> Permutation, Anzahl Zeichen: 2..9
xId1& = Trim$(X1)            ' // X1: in Permutation umzurechnender Index
xMsk$ = Trim$(X2)            ' // X2: Liste der Zeichen der Permutation, aufsteigend sortiert
xNnn& = Len(xMsk$)           ' // Anzahl unterschiedlicher Zeichen

' // Prüfen, ob die Länge von X2 in den Grenzen von 2 .. 9 liegt
If xNnn& < 2 Or xNnn& > 9 Then NummiX = "#n=2..9#": Exit Function

' // Prüfen, ob Index X1 in den Grenzen 1 .. n! liegt (n = Anzahl Zeichen laut Länge von X2)
xFak& = Fakultaet(xNnn&)
If xId1& < 1 Or xId1& > xFak& Then NummiX = "#Idx=1.." & xFak& & "#": Exit Function

' // Prüfen, ob in X2 nur Zeichen "ohne Wiederholung" enthalten sind
xPt1& = xNnn&
Do While xPt1& > 1
    If InStr(xMsk$, Mid$(xMsk$, xPt1&, 1)) < xPt1& Then NummiX = "#DblChr#": Exit Function
    xPt1& = xPt1& - 1
Loop

' // gesuchte Permutation aus dem Index X1 berechnen und in die Zeichen aus X2 wandeln
xLst$ = xMsk$                ' // Liste der zu permutierenden Zeichen
xId0& = xId1& - 1            ' // Zählung des Index ab 1 in Zählung ab 0 ändern
xErg$ = ""                   ' // ErgebnisVarable löschen
Do While xNnn& > 1
'    // Stelle/Zeichen berechnen
    xSte$ = Mid$(xLst$, Int(xId0& / Fakultaet(xNnn& - 1)) Mod xNnn& + 1, 1) ' // Stelle/Zeichen berechnen
    xErg$ = xErg$ & xSte$    ' // Zeichen dem Ergebnis hinzufügen
    xLst$ = Replace(xLst$, xSte$, "")  ' // verwendetes Zeichen aus Liste der möglichen Zeichen löschen
    xNnn& = xNnn& - 1
Loop
xErg$ = xErg$ & xLst$

NummiX = xErg$
End Function
' - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - +
Function IdxNummiX(X1, X2)   ' // Permutation --> Index, Anzahl Zeichen: 2..9
xEin$ = Trim$(X1)            ' // X1: in Index umzurechnende Permutation
xMsk$ = Trim$(X2)            ' // X2: Liste der permutierten Zeichen, aufsteigend sortiert
xNnn& = Len(xMsk$)           ' // Anzahl unterschiedliche Zeichen

' // Prüfen, ob die Länge von X2 in den Grenzen von 2 .. 9 liegt
If xNnn& < 2 Or xNnn& > 9 Then IdxNummiX = "#n=2..9#": Exit Function

' // Prüfen, ob in X2 nur Zeichen "ohne Wiederholung" enthalten sind
xPt1& = xNnn&
Do While xPt1& > 1
    If InStr(xMsk$, Mid$(xMsk$, xPt1&, 1)) < xPt1& Then IdxNummiX = "#DblChr#": Exit Function
    xPt1& = xPt1& - 1
Loop

' // Prüfen, ob Permutation X1 und Liste X2 der permutierten Zeichen zueinander passen
xLst$ = CReplac(xEin$, xMsk$, "")
xOk& = xNnn& = Len(xEin$) And Len(xLst$) = 0
If Not xOk& Then IdxNummiX = 0: Exit Function ' // Falls nicht korrekt: Abbruch mit RückgabeWert = 0

' // EingangsPermutation X1 in "numerisch" wandeln
xNum$ = ""
For ix& = 1 To xNnn&
    xNum$ = xNum$ & InStr(xMsk$, Mid$(xEin$, ix&, 1))
Next ix&

' // Index aus einer in eine Zahl gewandelten Permutation X1 berechnen
xRst$ = xNum$                          ' // in Zahl umgewandelte Permutation
xErg& = 1                              ' // Vorbesetzung wegen Zählung des Index ab 1 (statt 0)
xSor$ = Left$("123456789", xNnn&)      ' // sortierte Liste der Ziffern vorgeben
Do While xNnn& > 1
    xAkt$ = Left$(xRst$, 1)            ' // aktuelle Stelle isolieren
    xErg& = xErg& + Fakultaet(xNnn& - 1) * (InStr(xSor$, xAkt$) - 1) ' // Beitrag auf Index addieren
    xSor$ = Replace(xSor$, xAkt$, "")  ' // akt. Stelle aus Liste der sortierten RestZiffern löschen
    xRst$ = Replace(xRst$, xAkt$, "")  ' // akt. Stelle aus Liste der realen RestZiffern löschen
    xNnn& = xNnn& - 1
Loop

IdxNummiX = xErg&
End Function
' - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - +
' verwendete Funktionen:
' - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - +
Function Fakultaet(X1)
If X1 < 1 Then Fakultaet = 0: Exit Function 
xErg& = 1
For ix& = X1 To 2 Step -1
    xErg& = xErg& * ix&
Next ix&
Fakultaet = xErg&
End Function
' - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - +
Function CReplac(X1, X2, X3) '  // ersetzt alle in X2 enthaltenen Zeichen in X1 durch X3
xt$ = X1
xp& = 1
Do While xp& <= Len(xt$)
    If InStr(X2, Mid$(xt$, xp&, 1)) Then
        xt$ = Left$(xt$, xp& - 1) & X3 & Mid$(xt$, xp& + 1)
        xp& = xp& + Len(X3)
    Else
        xp& = xp& + 1
    End If
    Loop
CReplac = xt$
End Function
' - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - +
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Code:
xErg$ = ""                   ' // ErgebnisVarable löschen
Do While xNnn& > 1
'    // Stelle/Zeichen berechnen
    xSte$ = Mid$(xLst$, Int(xId0& / Fakultaet(xNnn& - 1)) Mod xNnn& + 1, 1) ' // Stelle/Zeichen berechnen
    xErg$ = xErg$ & xSte$    ' // Zeichen dem Ergebnis hinzufügen
    xLst$ = Replace(xLst$, xSte$, "")  ' // verwendetes Zeichen aus Liste der möglichen Zeichen löschen
    xNnn& = xNnn& - 1
Loop
xErg$ = xErg$ & xLst$
Das bestätigt mir, daß man einen Algorithmus formulieren kann. Bestätigt aber auch, daß der Algorithmus nicht so unmittelbar einleuchtend und effizient wie eine Lookup-Tabelle ist. Ich hatte aber ausdrücklich nach einer solchen Lösung gefragt. Vielen Dank für Deine Mühe. 👍

(Ich habe den Code jetzt (noch) nicht getestet, denke aber daß Du das ausgiebig gemacht hast.)

Harald
 
Das bestätigt mir, daß man einen Algorithmus formulieren kann. Bestätigt aber auch, daß der Algorithmus nicht so unmittelbar einleuchtend und effizient wie eine Lookup-Tabelle ist. Ich hatte aber ausdrücklich nach einer solchen Lösung gefragt. Vielen Dank für Deine Mühe. 👍
Moin, Harald, gern geschehen.
Leider traue ich mir nicht zu, einen Algorithmus, so zu formulieren, dass ihn auch ein Mathematiker abnicken könnte und habe mich deshalb damit begnügt, ihn in VBA zu "formulieren".
Das Verfahren ist deshalb leider nicht unmittelbar einleuchtend, weil es zeichenweise bzw. spaltenweise (wenn man die Tabelle Index/Permutation vor sich hat) von links nach rechts abgearbeitet wird und wegen "ohne Wiederholung" der Zeichen fortwährend weniger der Zeichen zur Auswahl stehen, sozusagen "aufgebraucht" sind.
Wenn man die Spalten von rechts nach links durchnumeriert (beginnend mit 1), sieht man, dass in der Spalte n die Ziffern nur alle (n-1)! wechseln und zwar zur nächst grösseren, sofern noch grössere zur Verfügung stehen.
Steht keine nächst grössere mehr zur Verfügung, dann steht man einer Position in der Tabelle, an der ohnehin in der Spalte n+1 ein Wechsel des Zeichens erfolgt ist und im folgenden "Block" eine neue Auswahl von Zeichen berücksichtigt werden muss. Hier gilt wieder aufsteigend sortiert, aber es sind Zeichen/Ziffern, die wiederum nicht lückenlos vorhanden sind.
Diese jeweils sortierte Liste steht in einem String und es wird nicht die PositionsNr in der Liste genommen, sondern das Zeichen/ die Ziffer, die auf dieser Position in der Liste steht. Statt des Strings kann man natürlich ein Array benutzen. Ich finde lediglich das Suchen eines Zeichens in einem String per 'Instring(' und das Lesen eines Zeichens an einer gewünschten Position per 'Mid(' und das Löschen eines Zeichens aus dem String per 'Replace' mit "automatischen Aufrücken" des restlichen Zeichen in VBA wesentlich einfacher und übersichtlicher als das Hantieren mit einem Array.
Wenn man aus einem String, in dem die Zeichen sortiert sind, ein oder mehrere Zeichen herauslöscht, bleibt der Inhalt des Strings sortiert.
Das ist eigentlich der einzige "Trick", den ich ausnutze.

Hast Du evtl. eine Idee, wie man dies "unmittelbar einleuchtend" formulieren könnte? Mir fehlen bisher die GeistesBlitze und die Worte. ;)

Der Zugriff über eine im Speicher "real existierende" Tabelle/Liste kann aber auch recht herausfordernd werden, da die Länge der Tabelle n! ist bei n Zeichen. Z.B. 362.880 Zeilen bei n = 9.
Spätestens bei der "umgekehrten Richtung", also Berechnung des Index aus einer gegebenen Permutation, fängt eine elendige Sucherei los.
Wegen der vorhandenen Sortierung drängt sich allerdings ein binäres Suchen auf.

Und wenn die Tabelle/Liste enorm viel Platz im Speicher belegt, dann fragt man sich "warum eine Tabelle/Liste spendieren, wenn ich's doch auch mit überschaubarem Aufwand berechnen kann?". Wer will freiwillig die Tabelle/Liste auf Richtigkeit bzw. Tippfehler prüfen?
Zugegeben, eine Tabelle/Liste wäre flexibler bezüglich der Werte in der Tabelle/Liste, aber bei der Aufgabenstellung dieses Threads kommt kein Bedarf dafür auf.

Allein schon wegen der Bedienbarkeit würde ich eher zu Jespers Vorschlag aus #35 neigen ...
Noch ein Vorschlag:
Anstatt das Thumbwheel, vier Buttons 1-2-3-4 und zwei Buttons Reset und Aktivieren.
Die Reihenfolge die sie betätigt werden bestimmen die Zahlenreihe.

Edit: Die Buttons können Farbanimation haben. Wenn ein Aggregat schon gewählt ist, wird der dazuhörige Button ausgegraut.

Anhang anzeigen 59667
... und, wie ich mich kenne, in VBA würde ich das Ganze auch wieder mit einem String lösen und dafür eine Replace-Funktion basteln, die so funktioniert, wie das Replace in VBA. ;)
 
Heini, ich kann nicht nachvollziehen wie dein Programm oben funktioniert. Aber in der wenn man so will "Brute-Force" Variante nimmt man den Heap-Algorithmus (beste/schnellste Variante) und verwendet die sich daraus ergebende Reihenfolge und lässt diesen bis zum gewünschten Index durchlaufen. Das entspricht dann zwar nicht der von PN/DP im 1.Post geforderten Reihenfolge, aber es ergibt sich wenigstens ein einigermaßen eleganter Code.

Das einzige was ich bisher aus diesen Thread sinnvoll mitgenommen habe ist, dass VBA noch schlimmer ist als Perl, und ich habe schon einiges in Perl programmiert.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Aber in der wenn man so will
1. "Brute-Force" Variante nimmt man den Heap-Algorithmus (beste/schnellste Variante) und
2. verwendet die sich daraus ergebende Reihenfolge und
3. lässt diesen bis zum gewünschten Index durchlaufen.
4. Das entspricht dann zwar nicht der von PN/DP im 1.Post geforderten Reihenfolge, aber es ergibt sich wenigstens ein einigermaßen eleganter Code.
Zu 1.:
Grunsätzlich: warum nicht?

Zu 2.:
Wenn's denn die geforderte Reihenfolge wäre: OK.

Zu 3.:
Durchlaufen? Das bedeutet aber, ich muss alle Permutationen beginnend mit dem Index 1 bis zum vorgegebenen Index i nacheinander ausführen, um das gesuchte Ergebnis zu erhalten!
Kann man machen. Kann man sich aber auch ersparen, indem man aus dem vorgegebenen Index i direkt die i-te Permutation berechnet, was bei der durch die Aufagbenstellung vorgegebene Reihenfolge der Permutationen (aufsteigend) durchaus möglich ist.

Zu 4. (und 1.):
Du willst also die Aufgabenstellung so umdefinieren, dass man den elegantesten Code verwenden kann, der dafür aber i-mal (von 1 bis i) durchlaufen werden muss, um die Lösung zum vorgegebenen Index i zu liefern.
Du hast Recht, Thomas. Das empfinde ich tatsächlich als "brute force".

Das einzige was ich bisher aus diesen Thread sinnvoll mitgenommen habe ist, dass VBA noch schlimmer ist als Perl, und ich habe schon einiges in Perl programmiert.
Ein abschreckendes Beispiel, programmiert in der Sprache A, zu nehmen und es mit einem sauberen Beispiel, programmiert in der Sprache B, zu vergleichen, das dürfte eigentlich immer dazu führen, dass die ProgrammierSprache A als VizeSieger aus dem Wettbewerb hervorgeht.
Dein Satz sagt eigentlich nichts über die beiden genannten ProgrammierSprachen aus, sondern nur, dass Dir meine Denkweise oder *) mein ProgrammierStil nicht gefallen.
Das hättest Du auch ganz direkt sagen können! ;)

Mein "Algorithmus" arbeitet auch mit Iteration. Aber die Anzahl der SchleifenDurchläufe (nämlich n-1) ist einzig durch die Anzahl n der zu permutierenden Zeichen vorgegeben.
1. Durchlauf berechnet die 1. Stelle des Ergebnisses
2. Durchlauf berechnet die 2. Stelle des Ergebnisses
u.s.w. bis
n-1 berechnet die vorletzte Stelle des Ergebnisses, wodurch dann zwangsläufig auch die letzte Stelle schon ermittelt ist.
Im Fall dieses Threads (4 Zeichen) also immer "bescheidene" 3 SchleifenDurchläufe (unabhängig vom Index).

Ein Teil des Aufwandes ergibt sich dadurch, dass der Index 1..24 sein soll statt der "eleganteren" 0..23.

Ein weiterer Teil des Aufwandes ergibt sich daraus, dass ich die Aufgabenstellung verallgemeinert habe (z.B. Onkel Dagobert hatte sich Flexibilität in der Stellenzahl gewünscht): die verwendeten Zeichen beschränken sich nicht mehr auf 1..n, sondern dürfen n beliebige Zeichen sein (Ziffern, GrossBuchstaben, KleinBuchstaben, Sonderzeichen, das alles beliebig durcheinander gemischt, jedoch maximal n=9 Zeichen).

Und nicht zuletzt, vor der eigentlichen Berechnung werden die beiden AufrufParameter der Funktion geprüft.
- Ist die Anzahl der verwendeten bzw. zu verwendenden Zeichen zulässig (n=2..9)?
- Enthält die vorgegebene Liste der zu verwendenden bzw. der verwendeten Zeichen unzulässige Wiederholungen?
- (Nur bei 'Berechnung der Permutation aus dem Index') Ist der vorgegebene Index bei der gewählten Anzahl Zeichen zulässig?
- (Nur bei 'Berechnung des Index aus der Permutation') Passt die vorgegebene Permutation bezüglich des Zeichensatzes zur vorgegebenen Liste der verwendeten Zeichen?

Ein schönes RWE **) wünscht

Heinileini

*) : inklusives(!) Oder.

**) : Nein, nicht RotWeissEssen und auch nicht RheinischWestfälischeElektrizitätswerke, sondern RestWochenEnde.
 
Das einzige was ich bisher aus diesen Thread sinnvoll mitgenommen habe ist, dass VBA noch schlimmer ist als Perl, und ich habe schon einiges in Perl programmiert.
Was an VBA oder dem Code von Heinileini findest Du "schlimmer"? Was ist für Dich "schlimm"? Müsstest Du da vielleicht VB generell verteufeln? Ich finde VB und VBA nicht schlecht und als durchaus seriös anwendbare Programmiersprache.

Harald
 
Moin,

@Heinileini : ich habe Deine Programmierung leider nicht ganz nachvollziehen können. Dazu fehlen mir vor allem die VBA-Kenntnisse. Deswegen habe ich mir die Mühe gespart, Deinen Code nachzuvollziehen.

Allerdings habe ich mir etwas überlegt, das ich hier zum Besten geben will. Leider funktioniert nur der Ansatz. Vielleicht kann Jemand meine Lücke schließen?

Folgender Ansatz:

Code:
Folge der umszusetzenden Nummern:

Nummern              Permutation   x = Fehlerstellen
------------------------------------------------------
00 - 00 - 00         1234        - 1234
01 - 01 - 01         1243        - 12xx
------- - --
02 - 02 - 00         1324        - 1xxx
03 - 03 - 01         1342         - 1xxx
-------
04 - 04 - 00         1423         - 1xxx
05 - 05 - 01         1432         - 1xxx
-- - --
06 - 00              2134         - 2134       
07 - 01              2143         - 21xx
08 - 02              2314         - 2314
09 - 03              2341         - 2341
10 - 04              2413         - 2xxx
11 - 05              2431         - 2xxx
-- - --
12 - 00              3124         - 3124
13 - 01              3142         - 31xx
14 - 02              3214         - 3214
15 - 03              3241         - 3241
16 - 04              3412         - 3412
17 - 05              3421         - 3421
-- - --
18 - 00              4123         - 4123
19 - 01              4132         - 41xx
20 - 02              4213         - 4213
21 - 03              4231         - 4231
22 - 04              4312         - 4312
23 - 05              4321         - 4321

Die Fehlerstellen entstehen m.E. durch die Problematik der Sortierung der verbleibenden
Ziffern. Aber ob ich genau richtig liege, müsste noch einmal betrachtet werden. Nichtsdesto-
trotz, wollte ich hier gerne meinen Ansatz mitteilen.

@Heinileini: Falls es der gleiche Ansatz ist, bei Deinem fertigen Algorithmus, entschuldige
ich mich hier schon einmal dafür.

Theorie:
Anzahl Stellen: 4 ==> Z
Nummer (0..23) ==> X
Stelle = 4321 ==> abcd
Bei Z! kommen 24 Möglichkeiten vor. Dabei kommt die erste von Z Stellen (Z-1)! oft vor.

Damit lässt sich die 4. Stelle (a) relativ einfach berechnen:
a = (X / (Z-1)!) + 1

Die 3. Stelle müsste die übriggebliebene Anzahl an Möglichkeiten = 6 (0..5) sein (siehe Tabelle oben)
Dazu die Nummer aus den sechs Möglichkeiten berechnen:
(X mod (Z-1)!)

Damit ergibt sich folgende Formel für die zweite Stelle:
b = ((X mod (Z-1)!) / (Z-2)!) + 1

Die 2. Stelle müsste die übriggebliebene Anzahl an Möglichkeiten = 2 (0..1) sein (siehe Tabelle oben)
Dazu die Nummer aus den sechs Möglichkeiten berechnen:
((X mod (Z-1)!) mod (Z-1)!)

Damit ergibt sich folgende Formel für die zweite Stelle:
c = (((X mod (Z-1)!) mod (Z-1)!) / (Z-3)!) + 1

Die 1. Stelle ist einfach die übrig gebliebene Ziffer.

Soviel zur Theorie, aber offensichtlich ergeben sich Fehler, die sich m.E. auf die Sortierung der übrig
gebliebenen Ziffern für die verbleibenden Stellen ergeben.


Also, ich habe dazu Folgendes programmiert:




#Ti_numberDigits := 4;

"DB_TEST".digitsAvailable[0] :=
"DB_TEST".digitsAvailable[1] :=
"DB_TEST".digitsAvailable[2] :=
"DB_TEST".digitsAvailable[3] :=
true;

"DB_TEST".digitsPool[0] := 1;
"DB_TEST".digitsPool[1] := 2;
"DB_TEST".digitsPool[2] := 3;
"DB_TEST".digitsPool[3] := 4;

// +++ Stelle 4 +++

// berechnen
#Ti_result :=
("DB_TEST".position
/ DINT_TO_INT("POOL_fakultaet"(#Ti_numberDigits - 1)))
+ 1;

// ausgeben
"DB_TEST".digits[0] := "DB_TEST".digitsPool[#Ti_result - 1];
"DB_TEST".digitsPool[#Ti_result - 1] := #Ti_result + 4;
"DB_TEST".digitsAvailable[#Ti_result - 1] := false;


// +++ Stelle 3 berechnen +++
//
#Ti_result :=
(
("DB_TEST".position MOD DINT_TO_INT("POOL_fakultaet"(#Ti_numberDigits - 1)))
/ DINT_TO_INT("POOL_fakultaet"(#Ti_numberDigits - 2)))
+ 1;

FOR #i := #Ti_result - 1 TO #Ti_numberDigits - 1 DO
    IF
        "DB_TEST".digitsAvailable[#i]
    THEN
        "DB_TEST".digits[1] := #i + 1;
        "DB_TEST".digitsAvailable[#i] := false;
        EXIT;
    END_IF;
END_FOR;

// +++ Stelle 2 berechnen +++
//
#Ti_result :=
(
(
("DB_TEST".position MOD DINT_TO_INT("POOL_fakultaet"(#Ti_numberDigits - 1))) // 1 mod 6 = 1
MOD DINT_TO_INT("POOL_fakultaet"(#Ti_numberDigits - 2))) // 1 mod 2 = 1
/ DINT_TO_INT("POOL_fakultaet"(#Ti_numberDigits - 3))) // 1 / 1 = 1
+ 1;

FOR #i := #Ti_result - 1 TO #Ti_numberDigits - 1 DO
    IF
        "DB_TEST".digitsAvailable[#i]
    THEN
        "DB_TEST".digits[2] := #i + 1;
        "DB_TEST".digitsAvailable[#i] := false;
        EXIT;
    END_IF;
END_FOR;

// +++ Stelle 1 ausgeben +++

FOR #i := 0 TO 3 DO
    IF
        "DB_TEST".digitsAvailable[#i]
    THEN
        EXIT;
    END_IF;
END_FOR;

"DB_TEST".digits[3] := #i + 1;


//-------------------------------------------------------------------------------------------------------------


FUNCTION "POOL_fakultaet" : DInt
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      Ii_val : Int;
   END_VAR

   VAR_TEMP 
      Tdi_faktor : DInt;
      Tdi_result : DInt;
   END_VAR


BEGIN
    
    
    
    #Tdi_faktor :=
    #Tdi_result :=
    INT_TO_DINT(#Ii_val);
    
    WHILE
        #Tdi_faktor > 1
    DO
        #Tdi_result *= (#Tdi_faktor - 1);
        #Tdi_faktor -= 1;
    END_WHILE;
    
    #POOL_fakultaet := #Tdi_result;
END_FUNCTION

VG

Mario
 
Zuviel Werbung?
-> Hier kostenlos registrieren
ich habe Deine Programmierung leider nicht ganz nachvollziehen können. Dazu fehlen mir vor allem die VBA-Kenntnisse. Deswegen habe ich mir die Mühe gespart, Deinen Code nachzuvollziehen.
Moin Mario,

umgekehrt geht es mir genauso, jedenfalls habe ich mir noch nicht die Zeit genommen, Deinen SCL-Code zu studieren.
Aber, ich glaube, dass wir mit unseren Überlegungen gaaanz nah beieinander sind.
Mit Deiner Beschreibung kam ich zuerst nicht klar, weil ich die Stellen von links nach rechts und Du offensichtlich von rechts nach links numeriert hast. Aber ich hab's dann recht schnell gemerkt ...
Anbei eine XL-Datei, in der die Permutationen mit den ArbeitsBlattFunktionen berechnet werden (ohne VBA).
Nur in Spalte D wird meine VBA-Funktion benutzt - zum Vergleich.
Ist ausgelegt für 5-stellige Kombinationen, aber das dürfte Dich nicht allzu sehr stören.

Gruss, Heinileini

Edit (2022-03-14 11:33): habe die XL-Datei aktualisiert/"personalisiert".

Edit (2022-03-14 13:40): echte pdf hinzugefügt (für alle, die Excel nicht haben/mögen) ;)
 

Anhänge

Zuletzt bearbeitet:
Moin Mario,

Danke daß sich noch jemand mit dem Thema befasst hat. Irgendwie erfordert die Aufgabe zwei Knicke in den Gehirnwindungen ;) weil der Wertevorrat für die jeweils übrigbleibenden Ziffern nicht zusammenhängend ist. Das bekommt man (bzw. ich) schwer in Mathematik formuliert. Die Verwendung von Stringfunktionen (wie Heinileini) erleichtert das etwas.

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
... weil der Wertevorrat für die jeweils übrigbleibenden Ziffern nicht zusammenhängend ist. Das bekommt man (bzw. ich) schwer in Mathematik formuliert.
Genau das war und ist auch mein Problem, wenn es darum geht, den Algorithmus zu formulieren.
Das Prinzip ist ganz simpel, aber wie erklärt man es am besten?

Ich konnte diesen Funktionalität selber verwenden. Ich habe auch Anlagen wo man die Reihenfolge für dei Befüllung und Entleerung von Silos wählen kann. Jetzt habe ich mein eigene Vorschlag programmiert. Habe eine halbe Stunde gedauert.
Wenn es jemand interessiert:
Anhang anzeigen 59782
Am besten man löst die Aufgabe so, dass das Problem gar nicht involviert ist.
Und die Bedienung wird dadurch auch viel einfacher und klarer! Danke Jesper!
 
@MFreiberger
Moin Mario,
habe nun die Umsortiererei statt mit StringBefehlen mit einem Array umgesetzt, allerdings in VBA.
VBA-Funktion EXCHG(X1, X2, X3) macht eine "TeilPermutation" (spaltenweise, nach und nach sozusagen) und
VBA-Funktion Mutat(X1, X2) führt in einer Schleife das aus, was die Array-Mimik in EXCHG tut und mutiert somit komplett.
Im TabellenBlatt, Spalten U .. X wird EXCHG aufgerufen und in Spalte AA wird Mutat aufgerufen.
X1 : ist die "niederwertigste" Permutation (entsprechend dem Ergebnis bei Index=0)
X2 : ist der Index (0..(n-1)!) zu dem die Permutation berechnet werden soll bzw. wird
X3 : ist die Spalten- bzw. StellenNr, die - wie Du es gemacht hast - beginnend mit 1 von rechts nach links numeriert ist.
Bei Mutat entfällt X3, weil Mutat die SpaltenNr intern als SchleifenZähler in der zusätzlichen Schleife "macht".

Beide Funktionen füllen am Anfang den eingelesenen String in ein Array um und am Schluss das Array wieder in einen String, der ausgegeben wird.
Die eigentliche "Kern" der Funktion steht dazwischen und ist vom Aufwand her recht überschaubar.
Code:
' // kompl. Permutation
For xSpa& = xTot& To 2 Step -1
    ' // TeilPermutation zu Idx0 und Spalte s
    xSol& = Int(xId0& / Fakultaet(xSpa& - 1)) Mod xSpa&
    xTmp& = a(xSpa& - 1 - xSol&)
    For ix& = xSpa& - 1 - xSol& To xSpa& - 2
         a(ix&) = a(ix& + 1)
    Next ix&
    a(xSpa& - 1) = xTmp&
Next xSpa&

Da nun die String-Trickserei entfällt, dürfte sich die VBA-Rechnerei wesentlich leichter in SCL umschreiben lassen.
Ich werde mich daran auch noch versuchen ...

Gruss, Heinileini
 

Anhänge

Zuletzt bearbeitet:
Jetzt habe ich mein eigene Vorschlag programmiert.
Anhang anzeigen 59782
Die Eingabe muß man in der SPS noch prüfen, ob jedes Symbol genau einmal vorkommt. Es gibt bestimmt Bediener, die klicken schneller als die Buttons unsichtbar werden ;) oder ein SPS-Programmierer steuert unkonzentriert direkt die Eingabewerte... Für die Prüfung braucht man mindestens n Operationen (muß jede Position anfassen). Bei der Variante mit der Übertragung des Index kann das nicht vorkommen, und da braucht man nur 2 Vergleiche auf die min/max-Indexgrenzen zur Prüfung.

( "Weihnachtsrätsel" B): wie kann man man effizient prüfen, ob in den 4 Positionen jedes Symbol genau einmal vorkommt? )

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
( "Weihnachtsrätsel" B): wie kann man man effizient prüfen, ob in den 4 Positionen jedes Symbol genau einmal vorkommt? )
In #42 habe ich es in VBA so gelöst:
Code:
' // Prüfen, ob in X2 nur Zeichen "ohne Wiederholung" enthalten sind
xPt1& = xNnn& ' // xNnn& = len(xMsk$)
Do While xPt1& > 1
    If InStr(xMsk$, Mid$(xMsk$, xPt1&, 1)) < xPt1& Then NummiX = "#DblChr#": Exit Function
    xPt1& = xPt1& - 1
Loop
Von rechts nach links werden n-1 der zu prüfenden Elemente gelesen und mit instr() im String xMsk$ (von links nach rechts) gesucht.
Ist die von instr() ermittelte Position kleiner, so kommt das Element links von der Position, wo das Element gelesen wurde, ein weiteres Mal vor und die Suche wird abgebrochen.

PS:
Wenn es sich bei den 4 "Symbolen" um die Ziffern 1..4 handelt/handeln sollte, dann muss das Produkt aus den 4 Symbolen = 4! sein.
 
Zuletzt bearbeitet:
Die Visibility Animation ist nur eine Hilfe für die Bediener. Wenn man ein Button 2-mal betätigt hat es keine Auswirkung. Man kann es eigentlich nicht falsch bedienen.
Die Code ist heir unten, relativ selbsterklärend.
Das schöne ist, ob 4, 5, 10 oder 20 Aggregate oder Silos, die Code wird nicht unterschiedlich.
Das einzigste was jetzt stört ist die kleine Verzögerung zwischen Buttonpress und die visibility Animation. Das konnte ich aber mit ein bischen Feinputzen verbessern.
Code:
FOR #i := 1 TO 4 DO
    IF "Aggregat".button_wahl[#i] AND NOT "Aggregat".naechste_gewaehlt[#i] THEN
        "Aggregat".naechste_Anzahl_gewaehlt := "Aggregat".naechste_Anzahl_gewaehlt + 1;
        "Aggregat".wahl_naechste["Aggregat".naechste_Anzahl_gewaehlt] := #i;
        "Aggregat".naechste_gewaehlt[#i] := TRUE;
        "Aggregat".button_wahl[#i] := FALSE;
    END_IF;
END_FOR;

IF "Aggregat".button_akzept THEN
    "Aggregat".wahl_aktiv := "Aggregat".wahl_naechste;
END_IF;

IF "Aggregat".button_reset OR "Aggregat".button_Akzept THEN
    FOR #i := 1 TO 4 DO
        "Aggregat".naechste_gewaehlt[#i] := FALSE;
        "Aggregat".wahl_naechste[#i] := 0;
    END_FOR;
    "Aggregat".naechste_Anzahl_gewaehlt := 0;
END_IF;

"Aggregat".button_reset := FALSE;
"Aggregat".button_akzept := FALSE;
 
Noch ein Aspekt: wenn man die mehreren Werte in mehrere verschiedene Variablen eingibt, dann ist bei der HMI-Kommunikation nicht garantiert daß die Werte gleichzeitig konsistent im selben Zyklus in der SPS ankommen. Da müssen die Variablen als Array in der HMI deklariert sein und hoffentlich wird das Array "im ganzen Stück" übertragen, oder man braucht ein Handshake oder eine Initialisierung der Variablen auf unmögliche Werte oder muß vor der Verarbeitung und ggf. Fehlermeldung etwas warten (wie lange??).

Ich will die schön bedienbare Lösung von Jesper nicht schlechtreden, habe aber so gewisse Bauchschmerzen... Bei Übertragung des Index der Permutationsliste in nur einer Variable können einige Probleme einfach nicht auftreten, deshalb gefällt mir diese Variante besser.

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Aggregat".button_wahl[#i] ist ein Array für die 4 Buttons am oben. Arrays werden immer komplett übertragen.
Ich denke aber dass wenn man versehentlich die falsche Reihenfolge geklickt hat, dann hat man die Möglichkeit das Wahl zu checken und entweder verwerfen oder übernehmen.

Bei die Dropdown Liste ist meine grössten Einwand dass mehr als 4 Aggregate wird es zu al zu viele Kombinationen. Die Liste wächst aus den Bildschirm und die Gefahr dass man die falsche Reihnefolge klickt steigt.
 
Zuletzt bearbeitet:
Zurück
Oben