# 1..24 umcodieren in Kombination von 4 Ziffern



## PN/DP (4 März 2022)

Ich muß mit einer S7-xxxx einen Zahlenwert 1..24 umcodieren in die 24 Möglichkeiten, 4 unterschiedliche Ziffern in einer Reihe anzuordnen:

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

Es soll ausdrücklich keine Lookup-Tabelle verwendet werden. Ich habe das jetzt mit einer Fallunterscheidung der 24 Fälle (CASE, bzw. in AWL mit SPL) programmiert. Geht das vielleicht auch kürzer? Kennt jemand eine Formel oder einen Algorithmus, um aus 1..24 (oder 0..23  ) die Kombination zu "berechnen" oder einen Bit Hack?

Ich habe das Gefühl, daß da was gehen müsste, komme aber nicht drauf. Im Internet finde ich keine Lösung, vielleicht verwende ich nicht die richtigen Suchworte?
Das Ergebnis darf als ein Dezimalwert oder BCD-Wert oder als 4 einzelne Werte herauskommen, es soll sowieso in 4 INT-Variablen zerlegt werden.

Harald


----------



## KLM (4 März 2022)

Kannst Du noch zwei, drei Worte zu den Ziel-Werten sagen? Gibt es Zusammenhänge zum Quell-Wert? Was spricht gegen ein Array mit Konstanten, und dem Quell-wert, als Index?


----------



## PN/DP (4 März 2022)

Die Zielwerte sind einfach die aufsteigend sortierte Liste der 24 Kombinationsmöglichkeiten.
Die 1..24 kommen aus einer Visu aus einer symbolischen Auswahlliste und sind der Index des ausgewählten Eintrags.
Ein Array mit Konstanten würde der Compiler auch nur in 24 Cases übersetzen, das Compilat würde nicht kürzer oder effizienter, ich hätte nur vielleicht etwas weniger Schreibarbeit (weniger Programmcode, dafür mehr Deklarationen).

Harald


----------



## KLM (4 März 2022)

Wenn es keinen mathematischen/logischen Zusammenhang zwischen Quell- und Ziel-Wert gibt, wirst Du auch keinen Berechnungsalgorithmus finden. Selbst wenn, tauschst Du CPU-Last gegen Schreibarbeit (Programmcode oder Deklaration). Ist dann eigentlich auch schon wurscht. Array fände ich persönlich bequemer, aber Case-Anweisung geht ebenso. Ob das für den Compiler, bzw. in der Abarbeitung das gleiche ist, würde ich aber bezweifeln. Was ist Dir wichtiger, Performance, Speichernutzung oder Programmieraufwand?


----------



## KLM (4 März 2022)

Wenn Du von "24 Kombinationsmöglichkeiten" sprichst, klingt das schon irgendwie nach Zusammenhang. Kannst Du das besser beschreiben? Hast Du 24 Checkboxen, die in Ihrer Kombination einen dezimalen Wert ergeben oder so etwas?


----------



## LargoD (4 März 2022)

quick and dirty

```
var
  i,j,k,l,m:Integer;
  Zahl:Array[1..4] of Integer;

begin
  for i := 1 to 4 do  begin
    Zahl[i] := 1;
    for j := 1 to 4 do begin
      if Zahl[j]=0 then   begin
        Zahl[j] := 2;
        for k := 1 to 4 do begin
          if Zahl[k]=0 then   begin
            Zahl[k] := 3;
            for l := 1 to 4 do begin
              if Zahl[l]=0 then   begin
                Zahl[l] := 4;
                Writeln(Zahl[1],Zahl[2],Zahl[3],Zahl[4]);
                Zahl[l] := 0;
              end;
            end;
            Zahl[k] := 0;
          end;
        end;
        Zahl[j] := 0;
      end;
    end;
    Zahl[i] := 0;
  end;
end;
```
Pascal ist ja gut lesbar

Gruß
Erich

p.s. : nochmal den Eingangstext gelesen und dann erst verstanden: Du suchst eine Funktion, die direkt die vierstellige Zahl liefert.
Vergiss meine Lösung


----------



## PN/DP (4 März 2022)

In einer Visu ist eine ausklappbare und dann scrollbare Liste (heißt wohl Kombinationsfeld?) mit 24 Einträgen/Zeilen, die mit den 24 Kombinationen 1234 bis 4321 aufsteigend sortiert beschriftet sind. Der Bediener wählt z.B. die 5. Zeile "1423", das liefert den Index 5 an die SPS, und die SPS muß nun die 5 umcodieren in 1423, und dann 1,4,2,3 in 4 Variablen schreiben. 

PS: Ich bin jetzt unterwegs zum Bierchen trinken  und melde mich wohl erst morgen wieder. 

Harald


----------



## ducati (4 März 2022)

PN/DP schrieb:


> Ich habe das jetzt mit einer Fallunterscheidung der 24 Fälle (CASE, bzw. in AWL mit SPL) programmiert.


Dann ist es schon fertig getippt? Welche Tipparbeit willst dann jetzt noch sparen 😉


----------



## PN/DP (4 März 2022)

Es ist bereits fertig. Aber aus Interesse und Spaß am Programmieren will ich wissen, ob es auch eine andere Lösung gibt. Vieeicht muß ich das mal wieder machen, dann vielleicht mit 5 Ziffern, da will ich evtl. nicht 120 Cases/Werte eintippen. 

Harald


----------



## KLM (4 März 2022)

Ich wüsste nichts effizienteres, als Array oder Case.
Prost!


----------



## JesperMP (4 März 2022)

PN/DP schrieb:


> Die 1..24 kommen aus einer Visu aus einer symbolischen Auswahlliste und sind der Index des ausgewählten Eintrags.


Die Lösung ist, nicht 1-24 als sondern die gewünschte Zahlenwerte 1234, 1243, 1324, 1342 usw . als das Index konfigurieren.
Einfach in die Textliste:
Index / Text
1234 / "1234"
1243 / "1243"
1324 / "1324"
usw.

Kein SPS Code notwendig, ausser die Umwandlung von den Zahlenwert ins einselne Ziffern muss man programmieren mittels INT --> BCD und dann Abfrage von die Einzelne BCD Ziffern.


----------



## JesperMP (4 März 2022)

PN/DP schrieb:


> Vieeicht muß ich das mal wieder machen, dann vielleicht mit 5 Ziffern, da will ich evtl. nicht 120 Cases/Werte eintippen.


Das Problem is wenn man kein normalen Eingabefeld hat, sondern ein Textliste weil man das Wahl den man eingeben kann begrenzen will.
Dann hat man ein Drop-Down der 120 Zeilen Tief ist, und es wird impraktikabel zu den unterste Wert zu scrollen.
In den Fall wäre es vielleicht besser mit ein virtuellen Codewheel (deutsche benennung ?).

Prost !


----------



## PN/DP (4 März 2022)

Die Visu ist WinCC 7.5, da kann man nicht festlegen welcher Index aus der Textauswahl-Liste zurückgegeben wird. (Schon das Eingeben der einzelnen Beschriftungen ist ziemlich mühselig...) 

Prost! 

Harald


----------



## Oberchefe (4 März 2022)

Permutation oder Kombinatorik ist das Stichwort. Das ist aber nicht wirklich einfach zu lösen. Hier ein Beispiel in VB6: https://www.vbarchiv.net/tipps/tipp_68-rekursive-permutation.html
Da ist aber ein rekursiver Aufruf der Funktion drin, d.h. du würdest einen FC in sich selber aufrufen. Zudem ist die erhaltene Augabe nicht aufsteigend sortiert, da müsste noch ein Bubblesort dahinter geschaltet werden. Ausgabe von dem verlinkten Beispiel:
`24 
1234
1243
1324
1423
1342
1432
2134
2143
3124
4123
3142
4132
2314
2413
3214
4213
3412
4312
2341
2431
3241
4231
3421
4321`


----------



## Thomas_v2.1 (4 März 2022)

Wenn es da eine Abkürzung bei Permutationen für Ziffern gäbe, dann vermute ich würde das irgendwo stehen. Selbst wenn man nur die Ziffern 1 und 2 verwendet also 12 und 21, fiele mir dafür keine Abkürzung ein die man dann auf mehr Ziffern ausrollen könnte.


----------



## Heinileini (5 März 2022)

Zwar keine "Abkürzung", aber in VBA(Excel) zusammengewurschtelt:

```
Function Nummi4(X1) ' X1 : 1..24; Ergebnis 1234..4321 (4-stellige Zahlen mit je 1 Ziffer aus 1, 2, 3, 4)
xMi1& = Max(1, Min(24, X1)) - 1
xZi4& = Int(xMi1& / 6) Mod 6 + 1
xCh3$ = Replace("1234", xZi4&, "")
xZi3& = Mid$(xCh3$, Int(xMi1& / 2) Mod 3 + 1, 1)
xCh2$ = Replace(xCh3$, xZi3&, "")
If xMi1& Mod 2 = 0 Then
    xZi1& = Right$(xCh2$, 1)
    xZi2& = Left$(xCh2$, 1)
Else
    xZi2& = Right$(xCh2$, 1)
    xZi1& = Left$(xCh2$, 1)
End If
Nummi4 = 1000 * xZi4& + 100 * xZi3& + 10 * xZi2& + xZi1&
End Function

' - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - +

Function Nummi5(X1) ' X1 : 1..120; Ergebnis 12345..54321 (5-stellige Zahlen mit je 1 Ziffer aus 1, 2, 3, 4, 5)
xMi1& = Max(1, Min(120, X1)) - 1
xZi5& = Int(xMi1& / 24) Mod 24 + 1
xCh4$ = Replace("12345", xZi5&, "")
xZi4& = Mid$(xCh4$, Int(xMi1& / 6) Mod 4 + 1, 1)
xCh3$ = Replace(xCh4$, xZi4&, "")
xZi3& = Mid$(xCh3$, Int(xMi1& / 2) Mod 3 + 1, 1)
xCh2$ = Replace(xCh3$, xZi3&, "")
If xMi1& Mod 2 = 0 Then
    xZi1& = Right$(xCh2$, 1)
    xZi2& = Left$(xCh2$, 1)
Else
    xZi2& = Right$(xCh2$, 1)
    xZi1& = Left$(xCh2$, 1)
End If
Nummi5 = 10000 * xZi5& + 1000 * xZi4& + 100 * xZi3& + 10 * xZi2& + xZi1&
End Function

' - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - +

Function Min(X1, X2)
If X1 < X2 Then Min = X1 Else Min = X2
End Function

Function Max(X1, X2)
If X1 > X2 Then Max = X1 Else Max = X2
End Function
```

PS:
Danke, Harald, für das verspätete WeihnachtsRätsel! 

PPS:
Glückwunsch, Harald, zum 17.777-ten Beitrag!


----------



## PN/DP (5 März 2022)

Bei dem Weihnachtsrätsel wird allerdings erwartet, daß es da auch eine Lösung gibt. Hier wird es wohl eher so sein, daß es keine sinnvolle Lösung außer Fallunterscheidung und Lookup-Table gibt. 😢 

Ich habe mir mal die Ergebniszahlen mit Excel angeschaut, ob da irgendeine berechenbare Regelmäßigkeit auffällt ... siehe Bild

Ich könnte mir nun noch vorstellen, daß der Code in einer Schleife die jeweils nächste Kombination berechnet/erzeugt bis zum gesuchten Index, vielleicht läßt sich das mit wenigen Regeln realisieren? Z.B. ausgehend von "1234" tauschen ja immer 2 Ziffern die Position (einmal Pos.3 mit Pos.4, dann Pos.4 mit Pos.2, dann wieder Pos.3 mit Pos.4, ...)

Harald


----------



## Heinileini (5 März 2022)

PN/DP schrieb:


> Ich habe mir mal die Ergebniszahlen mit Excel angeschaut, ob da irgendeine berechenbare Regelmäßigkeit auffällt ... siehe Bild
> ... vielleicht läßt sich das mit wenigen Regeln realisieren? Z.B. ausgehend von "1234" tauschen ja immer 2 Ziffern die Position (einmal Pos.3 mit Pos.4, dann Pos.4 mit Pos.2, dann wieder Pos.3 mit Pos.4, ...)


Ja, Harald, da fallen schon Regelmässigkeiten auf.
Die Differenzen zwischen zwei aufeinanderfolgenden Zahlen sind immer ein Vielfaches von 9.
Habe mir sogar schon die PrimFaktorenZerlegung der Differenzen angesehen und abgesehen von einigen Symmetrien ist mir noch nichts halbwegs Verwertbares auf gefallen.
Deine Behauptung, dass immer 2 Ziffern getauscht werden, stimmt so nicht, wenn die Zahlen in aufsteigend (oder absteigend) sortierter Reihenfolge betrachtet werden.
Anfangs hatte ich mir eine Rotation der Ziffern mit/mit ohne Tauschen von 2 Ziffern vorgestellt. Da habe ich noch nichts Einfaches gefunden, um die Sortierung zu gewährleisten (siehe auch den Beitrag #14 von Oberchefe).
Ich kann mich vage erinnern, dass ich mal eine solche Rotierei und Tauscherei auf einem hp25 (mit 4 Akkus) programmiert habe. Aber ich weiss nicht mehr mit welchem Ziel und mit welchen Eigenschaften ... ist viel zu lange her.

Hatte vor der Beschäftigung mit der Berechnung der n-stelligen Zahl aus dem Index versucht, zu einer gegebenen 4-stelligen Zahl die nächste bzw. vorherige in sortierter Reihenfolge zu berechnen. Hab's aber erstmal wieder eingestellt.

Bitte verzeih mir die übelsten impliziten DatenTypKonversionen in #16, die ich in VBA (gerne  ) nutze.
Das in SCL umzusetzen dürfte viel zusätzliche Tipperei erfordern. VBA finde ich sehr angenehm bei der Verarbeitung von Strings und davon mache ich auch hemmungslos Gebrauch an Stellen, an denen man diese als alles andere als naheliegend empfinden dürfte.

Z.Z. habe ich angefangen, den umgekehrten Weg einzuschlagen, nämlich aus der n-stelligen Zahl den zugehörigen Index zu berechnen.
Sieht nicht unlösbar aus, aber ganz schön abenteuerlich/aufwändig ...

Gruss, Heinileini


----------



## PN/DP (5 März 2022)

Heinileini schrieb:


> Deine Behauptung, dass immer 2 Ziffern getauscht, stimmt so nicht, wenn die Zahlen in aufsteigend (oder absteigend) sortierter Reihenfolge betrachtet werden.


Du hast recht, manchmal ändern auch mehr als 2 Ziffern die Position. Ich muß aber eine Liste in aufsteigend sortierter Reihenfolge verwenden.

Das Thema der Aufgabe ist durchaus recht interessant. Auch was dazu schon erforscht wurde, z.B.
https://de.wikipedia.org/wiki/Permutation
https://de.wikipedia.org/wiki/Fakultätsbasiertes_Zahlensystem
https://de.wikipedia.org/wiki/Fehlstand
Nur finde ich nicht, wie ich das für eine Formel oder einen Algorithmus nutzen könnte. 



Heinileini schrieb:


> Hatte vor der Beschäftigung mit der Berechnung der n-stelligen Zahl aus dem Index versucht, zu einer gegebenen 4-stelligen Zahl die nächste bzw. vorherige in sortierter Reihenfolge zu berechnen. Hab's aber erstmal wieder eingestellt.


Das wäre ein Ansatz für eine Schleifenlösung, die ich mir auch vorstellen könnte.

Harald


----------



## JesperMP (6 März 2022)

Also, es ist eine realen Augabe, kein Weihnachtsrätzel ?
Ich habe nichts geniales zu dem Thema.
Aber ich denke dass die Drop-Down liste kein gute Lösung für die Bediener ist. Schon bei 24 Kombination ist die Dropdown etwas (zu) tief.
Mit 5 Zifferen und 120 Kombinationen macht es kein Sinn mehr.
Besser die Aufgabe in ein anderen Weise lösen.

Erinnert Ihr die Thumbwheel (Daumenrad ?). Habe ich ungf. 1990 verwendet um ein Zahl in die SPS einzugeben, ohne Operatorpanel o.Ä.
So ein virtuellen Thumbwheel konnte man schnell programmieren, und die einzelne Ziffern sind dann in einzelne Variabeln.
Und man hat kein 120 stelligen Drop-down Liste. Kann 4, 5 oder 10 Zifferen haben ohne dass es komplizierter wird.


----------



## ducati (6 März 2022)

Was ist denn konkret die Aufgabe?
Ich hab sowas ähnliches abundzu mit Pumpenfolgen. Also in welcher Reihenfolge sollen 4 Pumpen lastabhängig nacheinander eingeschaltet werden?
Ich ignoriere dann einfach die ganzen nicht zwingend notwendigen Varianten. Bei mir gibts dann nur:
1234
2341
3412
4123

Manchmal ist weniger mehr...


----------



## JesperMP (6 März 2022)

Die Aufgabe ist, der Bediener soll ein 4-Stelligen Zahl wählen. 
Die einzelne Ziffern können nur 1-2-3-4 betragen. 
Jeden Wert kann nur einmal auftreten (kein 1-2-3-3 o.Ä).
In die SPS soll dann von das Zahl die einzelne Ziffern in separaten Variabeln geben.

Recht einfach. Es kann also alle Kombinationen vorkommen.
Ich wette es hat zu tun mit ein Pumpenreihenfolge, oder Siloreihenfolge o.Ä.

Das Problem ist nur, dass es durch ein Text-Liste in WinCC V7 gelöst ist, und in WinCC V7 ist das Index immer eine Reihenfolge ab 1, den man nicht beeinflussen kann.


----------



## Heinileini (6 März 2022)

JesperMP schrieb:


> Also, es ist eine realen Augabe, kein Weihnachtsrätzel ?


So, wie ich Harald verstanden habe, hat er einen realen Hintergrund für die Aufgabe.
Aber unabhängig davon oder dennoch ist es eine schöne Knobelei.
Das Wort Rätsel in dem Begriff WeihnachtsRätsel, na ja, kann man drüber streiten, ob das gerechtfertigt ist. Sudoku & Co werden ja auch als Rätsel bezeichnet.


JesperMP schrieb:


> Aber ich denke dass die Drop-Down liste kein gute Lösung für die Bediener ist. Schon bei 24 Kombination ist die Dropdown etwas (zu) tief.
> Mit 5 Zifferen und 120 Kombinationen macht es kein Sinn mehr.


Denke ich auch.


JesperMP schrieb:


> Besser die Aufgabe in ein anderen Weise lösen.
> Erinnert Ihr die Thumbwheel (Daumenrad ?).


Ja, ich erinnere mich sehr gut. Aber ein Stapel aus ThumbWheels macht die Eingabe auch nicht wirklich einfach. Man kann nicht entsprechend einer Eingabe einer Ziffer an einer Stelle dafür sorgen, dass diese Ziffer dann an den übrigen Stellen nicht mehr anwählbar ist.
Und die Eingabe der 6 Ziffern (bei einer 4-stelligen Zahl) oder der 5 Ziffern (bei einer 5-stelligen Zahl), die generell nicht zur Auswahl stehen, kann man auch nicht für die Eingabe sperren.
Schön übersichtlich ist nur, dass man die 4 oder 5 Ziffern nebeneinander sieht. Aber dafür braucht man keine ThumbWheels. Das leistet ein einfaches Eingabefeld auch.

Falls man unbedingt DropDownMenus für die Eingabe benutzen will, könnte man für jede einzelne Stelle der Zahl ein eigenes DropDownMenu spendieren.



JesperMP schrieb:


> Das Problem ist nur, dass es durch ein Text-Liste in WinCC V7 gelöst ist, und in WinCC V7 ist das Index immer eine Reihenfolge ab 1, den man nicht beeinflussen kann.


Na ja, ein grosses Problem ist das aber nicht. Spätestens wenn man mit Modulo rechnet, wünscht man sich zwar, dass die Zählung mit 0 beginnen möge, aber ggfs vorher eine 1 zu subtrahieren und danach wieder eine 1 zu addieren, das überfordert keinen Computer (nur uns Programmierer, wegen der zunehmenden Unübersichtlichkeit  ).

PS:
Ich habe jetzt LösungsWege gefunden, die umgekehrte UmwandlungsRichtung "n-stellige Zahl  --> zugehörigen  Index" zu berechnen für n = 4 und n = 5. Melde mich wieder ...

PPS:
Früher hatte ich mal den Eindruck, dass alle Programmierer den Beginn mit Index 0 bervorzugen, nur IBM war in dieser Beziehung anders gepolt und begann grundsätzlich mit dem Index 1. Und das war dann eben ein DeFaktoStandard.


----------



## Onkel Dagobert (6 März 2022)

Heinileini schrieb:


> .. Na ja, ein grosses Problem ist das aber nicht...


Was Jesper wahrscheinlich meint ist, dass in anderen Systemen wie WinCCflexible oder TIA-Portal in den Textlisten die "Textnummern" frei gewählt werden können (#11). Man wäre dort also nicht an die starre Definition "1, 2, 3, .. x", wie offensichtlich in WinCC üblich, gebunden. Das würde das Problem auf der Steuerungsseite gänzlich aufheben.


----------



## Heinileini (6 März 2022)

Onkel Dagobert schrieb:


> Was Jesper wahrscheinlich meint ist, dass in anderen Systemen wie WinCCflexible oder TIA-Portal in den Textlisten die "Textnummern" frei gewählt werden können (#11). Man wäre dort also nicht an die starre Definition "1, 2, 3, .. x", wie offensichtlich in WinCC üblich, gebunden. Das würde das Problem auf der Steuerungsseite gänzlich aufheben.


Hmmm. Also ein Array zur Verfügung haben, bei dem der Index für jeden Eintrag frei wählbar ist und dieser Index nicht fortlaufend durchnumeriert sein muss?
Vorstellbar, aber dadurch wird das Problem nur ins BetriebsSystem verschoben und der AnwendungsProgrammierer muss sich keinen Kopf machen.
Aber damit haben wir noch keinen bzw. nur einen Lösungsweg, wie das BetriebsSystem das auf die Reihe kriegt: eine Tabelle (notfalls HashCode mit allen Umwegen und Nachteilen). Aber dieser Lösungsweg war von Harald ausgeschlossen worden.


----------



## PN/DP (6 März 2022)

Ja, das ist für die Vorgabe einer Aggregate-Reihenfolge.
Die Auswahl mit nur einer symbolischen Liste hat den Vorteil, daß nicht aufwändig geprüft und verhindert werden muß, daß keine Ziffer mehrmals vorkommt, weil der Bediener das gar nicht eingeben kann. Bei 4 Aggregaten und 24 Möglichkeiten ist das noch gut bedienbar und schnell realisiert. Bei 5+ Aggregaten und 120+ Möglichkeiten wäre eine vollständige Liste aber tatsächlich zu lang. Da würde man sich besser auf nur ein paar Varianten beschränken, wie ducati in #21 erwähnte.

Wie gesagt, habe ich die Aufgabe der Decodierung/Umcodierung schon erledigt, und zwar in AWL mit SPL, weil ich keinen Algorithmus kenne und auch keinen auf die Schnelle finden konnte, der die Umcodierung durch eine Berechnung lösen kann. Jetzt kommt aber das Programmierer-Hobby  und meint, das müsste doch auch anders gehen, kann man nicht eine Formel oder einen Bit Hack dafür finden? Die Lookup-Tabelle habe ich ausgeschlossen, weil ich diese Lösung kenne und weil sonst womöglich nur diese Lösung vorgeschlagen würde. Es soll aber ausdrücklich eine Lösung abseits der Standard Lösungen gefunden werden. Das ist doch zumindest eine schöne Knobel-Aufgabe, oder vielleicht gibt es tatsächlich schon einen Algorithmus?

Schönen Sonntagnachmittag
Harald


----------



## Onkel Dagobert (6 März 2022)

Heinileini schrieb:


> .. Also ein Array zur Verfügung haben, bei dem der Index für jeden Eintrag frei wählbar ist und dieser Index nicht fortlaufend durchnumeriert sein muss? ..


Nein kein ARRAY, sondern eine Textliste ist gemeint. In WinCCflexible oder in TIA würde das selbstverständlich gehen, in WinCC offenbar nicht.
Aber du hast schon recht, danach ist nicht gesucht. Alles gut, Heinileini.

Harald, ich habe es mit drei verschachtelten FOR-Schleifen versucht, Modul [1..(N-1)!], Zeile, Spalte. Es wird mir aber zu kompliziert. Im richtigen Leben würde ich nach überschaubaren Lösungen suchen. Ich bin aber mal gespannt, was Heinileini daraus macht.


----------



## PN/DP (6 März 2022)

JesperMP schrieb:


> ich denke dass die Drop-Down liste kein gute Lösung für die Bediener ist. Schon bei 24 Kombination ist die Dropdown etwas (zu) tief.
> Mit 5 Zifferen und 120 Kombinationen macht es kein Sinn mehr.


Auswählen aus symbolischen Listen mit 100..200 Einträgen ist heutzutage durchaus als zumutbar eingestuft und wird z.B. zur Auswahl von Geburtsjahr oder aus einer Länderliste A... bis Z... häufig gemacht. Dann sind das aber meistens "Wisch"-Listen auf Touch-Bildschirmen, wo man "mit Schwung" drüberwischt und die Liste scrollt alleine 50 Einträge weiter. Das geht aber nicht mit WinCC 7.5, da reagiert das Auswahlfeld noch nicht mal auf das Eintippen der ersten Ziffer bzw. Anfangsbuchstabe.

Harald


----------



## Heinileini (6 März 2022)

PN/DP schrieb:


> Wie gesagt, habe ich die Aufgabe der Decodierung/Umcodierung schon erledigt, und zwar in AWL mit SPL, weil ich keinen Algorithmus kenne und auch keinen auf die Schnelle finden konnte, der die Umcodierung durch eine Berechnung lösen kann.
> 
> Die Lookup-Tabelle habe ich ausgeschlossen, ...


Die SPL-Variante ist ja die Methode "Lookup-Tabelle" und die bietet sich für die Berechnung in der "umgekehrten Richtung" schon gar nicht an.

Sooo, hier also meine VBA-Varianten für die Berechnung in der "umgekehrten Richtung" (Zahl --> Index):

```
Function IdxNummi4(X1)
xNum$ = Trim$(X1)
xLen& = Len(xNum$)
xOk& = xLen& = 4 And Len(Replace(Replace(Replace(Replace(xNum$, "1", ""), "2", ""), "3", ""), "4", "")) = 0
If Not xOk& Then IdxNummi4 = 0: Exit Function ' Falls Zahl nicht korrekt: Abbruch mit RückgabeWert = 0
For ix& = 1 To xLen&
    xSt4$ = xSt4$ + Chr$(ix& + 48)
Next ix&
xSt3$ = Replace$(xSt4$, Mid$(xNum$, 1, 1), "")
xSt2$ = Replace$(xSt3$, Mid$(xNum$, 2, 1), "")
xPt3& = 6 * (Mid$(xNum$, 1, 1) - 1)
xPt2& = Int((InStr(Right$(xSt3$, 2) & " " & Left$(xSt3$, 1) & Right$(xSt3$, 1) & " " & Left$(xSt3$, 2), xSt2$) - 1) / 3) * 2
xPt1& = Sgn(Mid$(xNum$, 3, 2) - xSt2$)
IdxNummi4 = xPt3& + xPt2& + xPt1& + 1
End Function

' - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - +

Function IdxNummi5(X1)
xNum$ = Trim$(X1)
xLen& = Len(xNum$)
xOk& = xLen& = 5 And Len(Replace(Replace(Replace(Replace(Replace(xNum$, "1", ""), "2", ""), "3", ""), "4", ""), "5", "")) = 0
If Not xOk& Then IdxNummi5 = 0: Exit Function ' Falls Zahl nicht korrekt: Abbruch mit RückgabeWert = 0
For ix& = 1 To xLen&
    xSt5$ = xSt5$ + Chr$(ix& + 48)
Next ix&
xSt4$ = Replace$(xSt5$, Mid$(xNum$, 1, 1), "")
xSt3$ = Replace$(xSt4$, Mid$(xNum$, 2, 1), "")
xSt2$ = Replace$(xSt3$, Mid$(xNum$, 3, 1), "")
xPt4& = 24 * (Mid$(xNum$, 1, 1) - 1)
xPt3& = Int((InStr(Replace$(xSt4$, Mid$(xSt4$, 1, 1), "") & " " & Replace$(xSt4$, Mid$(xSt4$, 2, 1), "") & " " & Replace$(xSt4$, Mid$(xSt4$, 3, 1), "") & " " & Replace$(xSt4$, Mid$(xSt4$, 4, 1), ""), xSt3$) - 1) / 4) * 6
xPt2& = Int((InStr(Right$(xSt3$, 2) & " " & Left$(xSt3$, 1) & Right$(xSt3$, 1) & " " & Left$(xSt3$, 2), xSt2$) - 1) / 3) * 2
xPt1& = Sgn(Mid$(xNum$, 4, 2) - xSt2$)
IdxNummi5 = xPt4& + xPt3& + xPt2& + xPt1& + 1
End Function
```
Das mutet zwar mehr nach "TextVerarbeitung" an als nach Algorithmus, aber ... na ja, what shall's.

Edit (2022-03-07; 14:40):
Die "SchrottZeile"

```
Replace$(xSt4$, Mid$(xSt4$, 3, 1), "") & " " & Replace$(xSt4$, Mid$(xSt4$, 4, 1), "")
```
aus IdxNummi4 entfernt.
Habe keine Ahnung, warum die durch copy/paste dazu gezaubert wurde.


----------



## Heinileini (6 März 2022)

Onkel Dagobert schrieb:


> Nein kein ARRAY, sondern eine Textliste ist gemeint.


Das ist aber schon fast ein Streit um die Formulierung, Dagobert.
Sogar eine TextListe kann man als Variante eines Arrays betrachten. Man gibt den Zeilen der Liste eine fortlaufende Numerierung und schon sind diese beiden Welten vereint.


Onkel Dagobert schrieb:


> Ich bin aber mal gespannt, was Heinileini daraus macht.


Dein banges Warten kannst Du eigentlich beenden, Dagobert. 
Das Ergebnis hatte ich doch gestern in #16 gepostet. Zugegeben, nicht in KOP oder FUP oder SCL, aber immerhin in VBA.
Da ich ausgiebig von impliziten DatentypKonversionen Gebrauch gemacht habe (wie auch im meinem Beitrag #19 von vorhin), habe ich den Quark noch nicht in SCL umformuliert, aus Zeitmangel und reiner Bequemlichkeit.  

Und nein, ich bin nicht faul, sondern lediglich im EnergieSparModus!


----------



## PN/DP (6 März 2022)

Heinileini schrieb:


> Die SPL-Variante ist ja die Methode "Lookup-Tabelle"


Die SPL-Variante arbeitet zwar auch mit vorberechneten Werten, mit Lookup-Tabelle meine ich aber eine vorbereitete Tabelle (Array) und das Programm entnimmt direkt das durch den Index 1..14 adressierte Element: `Code := Tabelle[Index];`

Harald


----------



## Heinileini (6 März 2022)

PN/DP schrieb:


> Die SPL-Variante arbeitet zwar auch mit vorberechneten Werten, mit Lookup-Tabelle meine ich aber eine vorbereitete Tabelle (Array) und das Programm entnimmt direkt das durch den Index 1..14 adressierte Element: `Code := Tabelle[Index];`


Ja, so gesehen und genau genommen, ähnelt die SPL-Variante eigentlich eher einer CASE-Selektion, mit der man das Vorhandensein einer Tabelle/Liste vorgaukelt bzw. ersetzt.


----------



## JesperMP (6 März 2022)

Heinileini schrieb:


> Aber ein Stapel aus ThumbWheels macht die Eingabe auch nicht wirklich einfach. Man kann nicht entsprechend einer Eingabe einer Ziffer an einer Stelle dafür sorgen, dass diese Ziffer dann an den übrigen Stellen nicht mehr anwählbar ist.
> Und die Eingabe der 6 Ziffern (bei einer 4-stelligen Zahl) oder der 5 Ziffern (bei einer 5-stelligen Zahl), die generell nicht zur Auswahl stehen, kann man auch nicht für die Eingabe sperren.


Ich wurde das Thumbwheel mit eine Übernahme Button kombinieren. Und die Button sollte nur bedienbar sein wenn eine erlaubte Kombination gewählt ist.
Genau wie bei ein realen Thumbwheel.


----------



## Heinileini (6 März 2022)

PN/DP schrieb:


> Auswählen aus symbolischen Listen mit 100..200 Einträgen ist heutzutage durchaus als zumutbar eingestuft


Jooo, von den Programmierern akzeptiert mit Euphorie, von den Benutzern aber eher zaghaft und widerwillig. 🤪


----------



## JesperMP (7 März 2022)

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.


----------



## Onkel Dagobert (7 März 2022)

Heinileini schrieb:


> Das ist aber schon fast ein Streit um die Formulierung, Dagobert.
> Sogar eine TextListe kann man als Variante eines Arrays betrachten. Man gibt den Zeilen der Liste eine fortlaufende Numerierung und schon sind diese beiden Welten vereint...


Heinileini, ich versuche dir die ganze Zeit zu erklären, dass in WinCCflexible und im TIA-Portal bei Textlisten *KEINE* fortlaufende Numerierung notwendig ist.



Heinileini schrieb:


> ... Das Ergebnis hatte ich doch gestern in #16 gepostet ...


Wenn ich 2 und 2 zusammen zähle, erhalte ich auch ein Ergebnis. Aber ist das die gesuchte Lösung?


----------



## Heinileini (7 März 2022)

Onkel Dagobert schrieb:


> Heinileini, ich versuche dir die ganze Zeit zu erklären, dass in WinCCflexible und im TIA-Portal bei Textlisten *KEINE* fortlaufende Numerierung notwendig ist.


Das habe ich doch verstanden, Dagobert. 
Wenn ich das gerne möchte, kann ich doch den Zeilen (Einträgen) einer Liste nach eigenem Gutdünken eine eigene laufende Nr hinzufügen und die Liste unter Verwendung dieser Numerierung als Index in ein Array eintragen. Wo ist das Problem?


Onkel Dagobert schrieb:


> Wenn ich 2 und 2 zusammen zähle, erhalte ich auch ein Ergebnis. Aber ist das die gesuchte Lösung?


Woher soll ich das Wissen? Hast Du mir schon verraten, wofür Du eine Lösung suchst und ich habe es nur übersehen?
In welcher Form hättest Du denn gern die Lösung zu der von Harald gestellten Aufgabe?


----------



## Onkel Dagobert (7 März 2022)

Heinileini schrieb:


> .. In welcher Form hättest Du denn gern die Lösung zu der von Harald gestellten Aufgabe?


Ich stelle mir einen richtigen Algorithmus vor, der flexibel in der Stellenzahl ist, der mehr rechnet als schiebt und ersetzt.



Heinileini schrieb:


> .. als Index in ein Array eintragen. Wo ist das Problem? ...


Das war ja nun von vorn herein ausgeschlossen.


----------



## Heinileini (7 März 2022)

Onkel Dagobert schrieb:


> Ich stelle mir einen richtigen Algorithmus vor,
> 1. der flexibel in der Stellenzahl ist,
> 2. der mehr rechnet als schiebt und ersetzt.


Zu 1.: Wird angestrebt, ist aber nicht realisiert.
Zur Zeit strebe ich ferner an, mit Kombinationen aus beliebigen Zeichen zu arbeiten, also die Beschränkung auf n fortlaufend aufeinanderfolgenden Ziffern, beginnend mit 1, aufzuheben (davon ist in Beitrag #16 noch nichts zu sehen).

Zu 2.: mehr Arithmetik? Allen Ernstes?
In meinem Beitrag #16 benutze ich:
- Addition,
- Subtraktion,
- Multiplikation,
- Division,
- Modulo
Ich könnte auch mit Fakultäten arbeiten, habe aber stattdessen erstmal Konstanten verwendet, um die Formeln nicht noch umfangreicher und noch unübersichtlicher werden zu lassen (2! = 2; 3! = 6; 4! = 24; 5! = 120).
Ferner benutze ich Vergleiche u.a. zur Realisierung von
- Limit unter zuHilfeNahme der Funktionen
- Min und
- Max.

Was stört Dich an "Schieben und Ersetzen"?
Kombinatorik hat durchaus mit Rotieren/Schieben und Tauschen/Ersetzen zu tun.
Ich glaube, Dagobert, es sind eher die in der Aufgabestellung lauernden BegleitUmstände, die nicht nach Deinem Geschmack sind, als mein bescheidener LösungsAnsatz für
1. die Aufgabenstellung mit 4 Ziffern und
2. meine "Zugabe", sinngemäss dasgleiche mit 5 Ziffern.



Onkel Dagobert schrieb:


> Das war ja nun von vorn herein ausgeschlossen.


Der Index war *nicht* ausgeschlossen. Im Gegenteil, aus dem Index soll laut Aufgabenstellung das Ergebnis berechnet werden.
Zwar wird das Wort Index in der Aufgabenstellung nicht benutzt, aber es wird einem quasi auf die Zunge gelegt, wo Du es Dir zergehen lassen darfst.
Bei der Lösung soll keine Tabelle benutzt werden, ABER zur Klarstellung der Aufgabe hat Harald eine Tabelle angedeutet, naheliegenderweise.

Übrigens, mein Beitrag #16 arbeitet ohne jegliches Array, benutzt aber Strings, in denen *list*igerweise Ziffern aufge*liste*t sind.
Selbstverständlich kann man aber den Inhalt eines Strings als ein Array von Zeichen deuten ... wenn man unbedingt will.

Hast Du denn schon mal an einer Lösung der Aufgabe herumgeknobelt? Oder wartest Du lieber weiter darauf, dass hier die PatentLösung nach Deinem Geschmack vom Himmel fällt?


----------



## Onkel Dagobert (8 März 2022)

Oh je, bin ich mal wieder ins Fettnäpfchen getreten? Entschuldige!



Heinileini schrieb:


> Hast Du denn schon mal an einer Lösung der Aufgabe herumgeknobelt? Oder wartest Du lieber weiter darauf, dass hier die PatentLösung nach Deinem Geschmack vom Himmel fällt?


Ja, ich hatte am Wochenende ein wenig daran herumgeknobelt. Allerdings musste ich mir recht bald eingestehen, dass ich nicht auf eine Lösung "nach meinem Geschmack" kommen werde. Da ich so etwas mit Sicherheit auch völlig anders lösen würde, vor allem auch bedienungsfreundlicher, interessiert mich eine Patentlösung nicht wirklich. Rein programmtechnisch bin ich natürlich schon an Lösungen interessiert, denen man etwas abgewinnen kann.


----------



## Thomas_v2.1 (8 März 2022)

PN/DP schrieb:


> 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.


----------



## Heinileini (11 März 2022)

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.



PN/DP schrieb:


> 1 --> 1234
> 2 --> 1243
> 3 --> 1324
> 4 --> 1342
> ...



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


```
' - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - +
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
' - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - +
```


----------



## PN/DP (12 März 2022)

Heinileini schrieb:


> ```
> xErg$ = ""                   ' // ErgebnisVarable löschen
> Do While xNnn& > 1
> '    // Stelle/Zeichen berechnen
> ...


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


----------



## Heinileini (12 März 2022)

PN/DP schrieb:


> 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 ...


JesperMP schrieb:


> 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.
> 
> ...


... 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.


----------



## Thomas_v2.1 (13 März 2022)

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.


----------



## Heinileini (13 März 2022)

Thomas_v2.1 schrieb:


> 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".



Thomas_v2.1 schrieb:


> 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.


----------



## PN/DP (13 März 2022)

Thomas_v2.1 schrieb:


> 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


----------



## MFreiberger (14 März 2022)

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:


```
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


----------



## Heinileini (14 März 2022)

MFreiberger schrieb:


> 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)


----------



## PN/DP (14 März 2022)

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


----------



## JesperMP (14 März 2022)

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:


----------



## Heinileini (14 März 2022)

PN/DP schrieb:


> ... 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?



JesperMP schrieb:


> 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!


----------



## Heinileini (15 März 2022)

@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.

```
' // 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


----------



## PN/DP (15 März 2022)

JesperMP schrieb:


> 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


----------



## Heinileini (15 März 2022)

PN/DP schrieb:


> ( "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:

```
' // 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.


----------



## JesperMP (15 März 2022)

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.

```
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;
```


----------



## PN/DP (15 März 2022)

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


----------



## JesperMP (15 März 2022)

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.


----------



## PN/DP (15 März 2022)

Ja, alle Lösungen haben Vorteile und Nachteile und wir müssen uns für eine Lösung entscheiden. 

Harald


----------



## PN/DP (15 März 2022)

Heinileini schrieb:


> 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.


4! = 24
2 * 2 * 2 * 3 = 24

Harald


----------



## Heinileini (15 März 2022)

Heinileini schrieb:


> 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 ...


Habe ich mittlerweile (wie immer ungestestet):

```
' // kompl. Permutation (Beispiel für 5-stellige Permutationen)

CONST
    maxcol : INT   := 5 ;           // Anzahl Stellen der Permutation
    maxidx : INT   := maxcol - 1 ;  // für die Dimensionierung des Array
END_CONST

VAR_INPUT                       
    idx1   : INT   ;                // 1..maxcol!
END_VAR

VAR_TEMP
    idx0   : INT   ;                // 0..(maxcol! - 1)
    col    : INT   ;                // aktuelle Stelle/Spalte; Zählung beginnt rechts mit 1
    tmp    : INT   ;
    ord    : INT   ;                // OrdnungsZahl 0..4 für die berechnete Ziffer für die gegebene Stelle und den gegebenen Index
    idx    : INT   ;                // Array-Index             
    arr    : ARRAY [0..maxidx] OF BYTE := 5, 4 , 3 , 2 ,1 ; // "niederwertigste" Permutation
END_VAR

idx0 = idx1 - 1 ;

FOR col := maxcol TO 2 BY -1 DO

    ord := (idx0 / Fakultaet(col - 1)) MOD col ;
    tmp := arr(col - 1 - ord) ;
    FOR idx = col - 1 - ord TO col - 2 DO    // (Schleife darf nicht ausgeführt werden, wenn col - 1 - ord > col - 2 ist)
         arr(idx) = arr(idx + 1) ;
    END_FOR ;
    arr(col - 1) = tmp ;

END_FOR ;

// Die Elemente des Array enthalten jetzt die gesuchte Permutation ...
// ... aber wohin damit? Keine Ahnung. So gesehen war die StringVariante doch einfacher.
```
Den blöden Kommentar "(Schleife darf nicht ausgeführt werden, wenn col - 1 - ord > col - 2 ist)" habe ich eingefügt, weil ich nicht weiss, wie sich FOR in SCL verhält  und andernfalls Mehraufwand erforderlich ist.


----------



## Oberchefe (15 März 2022)

> 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.



Zur Not kann man die 4 Ziffern im HMI zu einer 4-stelligen Zahl kombinieren


----------



## PN/DP (15 März 2022)

Ja, sollte man. Und das zusammenklicken der 4 Ziffern nur mit Panel-internen Variablen machen und erst das Ergebnis in PLC-Variablen schreiben.

Harald


----------



## sunny22 (31 März 2022)

Hier mal eine "Bitschubser" Version ohne Schleifen in SCL.


> FUNCTION "permut" : Int
> { S7_Optimized_Access := 'FALSE' }
> VERSION : 0.1
> VAR_INPUT
> ...


EDIT: Wenn man sich nicht an langen Produkttermen stört, geht das auch ganz linear ohne IF.


> #tindex := INT_TO_BYTE(IN := #index);
> #atPerm[0] := (NOT #atindex[3] AND NOT #atindex[1] AND #atindex[0]) OR (NOT #atindex[3] AND #atindex[1] AND NOT #atindex[0]) OR (NOT #atindex[3] AND NOT #atindex[2] AND NOT #atindex[1]) OR (#atindex[3] AND #atindex[1] AND #atindex[0]) OR (#atindex[3] AND #atindex[2] AND NOT #atindex[1] AND NOT #atindex[0]);
> #atPerm[1] := (#atindex[4] AND #atindex[3]) OR (#atindex[3] AND NOT #atindex[2] AND #atindex[0]) OR (#atindex[3] AND NOT #atindex[2] AND #atindex[1]) OR (NOT #atindex[4] AND NOT #atindex[2] AND #atindex[1] AND #atindex[0]) OR (NOT #atindex[4] AND NOT #atindex[3] AND #atindex[2] AND NOT #atindex[1]) OR (NOT #atindex[4] AND NOT #atindex[3] AND #atindex[2] AND NOT #atindex[0]) OR (NOT #atindex[4] AND #atindex[2] AND NOT #atindex[1] AND NOT #atindex[0]) OR (#atindex[4] AND NOT #atindex[2] AND NOT #atindex[1] AND #atindex[0]) OR (#atindex[4] AND NOT #atindex[2] AND #atindex[1] AND NOT #atindex[0]) OR (#atindex[4] AND #atindex[2] AND #atindex[1] AND #atindex[0]);
> #atPerm[4] := (#atindex[3] AND NOT #atindex[2]) OR (#atindex[4] AND #atindex[2]) OR (#atindex[3] AND NOT #atindex[1] AND NOT #atindex[0]) OR (#atindex[4] AND #atindex[1] AND #atindex[0]) OR (NOT #atindex[3] AND #atindex[2] AND #atindex[1] AND #atindex[0]);
> ...


----------



## Heinileini (3 April 2022)

sunny22 schrieb:


> Hier mal eine "Bitschubser" Version ohne Schleifen in SCL.


Leider kann ich das Progrämmchen nicht testen und die BitSchubsereien waren mir anfangs zu mühsam *), sie nachzuvollziehen.
Was mir gefällt ist die Ausnutzung der "Symmetrie" und die damit verbundene "Nicht-Einsparung" der IF-Abfragen.
Ich finde aber, dass Du die Symmetrie nicht konsequent ausgenutzt hast, insofern ...
- Du #atPerm[5] := x mit in die If-Abfrage einbezogen hast und
- #tPerm := #tPerm XOR 16#1333; vermutlich haarscharf daneben sein dürfte.
Ich hätte 16#*3*333 genommen und #atPerm[5] := 0 ... unabhängig von Index > 12.

Habe versucht Deine Fassung ein wenig augenfreundlicher umzugestalten.
Und die Verknüpfungen zu vereinfachen (mit XOR) ... wodurch leider die Augenfreundlichkeit wieder nachgelassen hat  :

```
BEGIN
IF #index > 12 THEN
    #tindex     := INT_TO_BYTE(IN := 24 - #index) ; // Zuweisung #atPerm[5] verlegt, s.u. D1
ELSE
    #tindex     := INT_TO_BYTE(IN := #index - 1) ;  // Zuweisung #atPerm[5] verlegt, s.u. D1
END_IF ;

//                  -   -  D1  D0    -   -  C1  C0     -   -  B1  B0    -   -  A1  A0
//  #atPerm[]      [7] [6] [5] [4]  [3] [2] [1] [0]   [F] [E] [D] [C]  [B] [A] [9] [8]

//                  -   -   -   -    d   c   b   a
//  #atindex[]     [7] [6] [5] [4]  [3] [2] [1] [0]

//  D1 = 0
#atPerm[5]  :=      0 ;                 // hinzugefügt
//  D0 = d + cb
#atPerm[4]  :=      #atindex[3]
            OR      #atindex[2] AND      #atindex[1] ;
//  C1 = d + /cb + c/b
//     = d + (c # b)                    // # : XOR       
#atPerm[1]  :=      #atindex[3]
            OR     (#atindex[2] XOR      #atindex[1]) ;
//  C0 = /d/b + db
//     = /d # b                         // # : XOR
#atPerm[0]  :=  NOT #atindex[3] XOR      #atindex[1] ;
//  B1 = a + cb + /d/c/b           
#atPerm[13] :=      #atindex[0]
            OR      #atindex[2] AND      #atindex[1]
            OR  NOT #atindex[3] AND  NOT #atindex[2] AND  NOT #atindex[1] ;
//  B0 = /c/ba + /d/cb + /dba + c/b/a
//     = /b*(c # a) + /db*(/c + a)      // # : XOR        
#atPerm[12] :=  NOT #atindex[1] AND     (#atindex[2] XOR      #atindex[0])
            OR  NOT #atindex[3] AND      #atindex[1] AND (NOT #atindex[2] XOR     #atindex[0]) ;
//  A1 = /a + cb + /d/c/b          
#atPerm[9]  :=  NOT #atindex[0]
            OR      #atindex[2] AND      #atindex[1]
            OR  NOT #atindex[3] AND  NOT #atindex[2] AND  NOT #atindex[1] ;
//  A0 = /c/b/a + /d/cb + /db/a + c/ba
//     = /db*(/c + /a) + /b*(/c # a)    // # : XOR        
#atPerm[8]  :=  NOT #atindex[3] AND      #atindex[1] AND (NOT #atindex[2] OR  NOT #atindex[0])
            OR  NOT #atindex[1] AND (NOT #atindex[2] XOR      #atindex[0]) ;

IF #index > 12 THEN
    #tPerm := #tPerm XOR 16#3333 ; // warum zuvor 16#1333 und bedingte Zuweisung von #atPerm[5] ?
END_IF ;

#permut := DINT_TO_INT(IN := BCD32_TO_DINT(IN := #tPerm)) + 1111 ;
END_FUNCTION
```
Ich hatte einen ganz ähnlichen Ansatz begonnen und wieder verworfen, weil er für meinen Geschmack zu sehr auf die Permutationen von *4* Zeichen spezialisiert war und weil ich die Verwendung einer Schleife für flexibler und "näher an einem Algorithmus" gehalten habe.
Ein Bestandteil meines Ansatzes war, intern nicht den IndexBereich 1..12 bzw. 0..11, sondern 2..13 zu verwenden, weil ich dachte/hoffte damit die WahrheitsTabelle etwas vereinfachen zu können. Aber wenn ich das EinsparPotenzial bei Deinem Lösungsweg so sehe (XOR-Verknüpfungen), dann war ich wohl auf dem Holzweg ...

*) Habe inzwischen doch die Mühe auf mich genommen.


----------



## sunny22 (4 April 2022)

Heinileini schrieb:


> Ich hätte 16#*3*333 genommen und #atPerm[5] := 0 ... unabhängig von Index > 12.


Das hatte ich nicht auf dem Schirm. Es geht halt immer noch etwas kürzer, besser oder schneller. Genau wie mit dem XOR.


----------

