Anzahl Elemente in Array hochzählen

H

h.nietnagel

Grünschnabel
Hallo zusammen,

nach etwa 20 Stunden unermüdlichen Probierens, komme ich jetzt doch nicht am Nachfragen vorbei :-(

Ich möchte ein Array von 16 Dateinamen erstellen, die eine Zahl enthalten (zwischen 0 und 15), die zufällig vergeben werden soll, allerdings soll jede nur einmal vergeben werden.
Wenn ich einen "break" einfüge, nachdem die Zahl zweimal vorkommt, dann wird dieser ignoriert und die Zahl kann häufiger vergeben werden, wenn ich einen "break 2" einfüge, dann enthält mein Array zum Schluss weniger als die 16 Einträge.
Wo liegt mein Denkfehler?
PHP:
#!/bin/bash
#Program to crop the picture and resamble the pieces
declare -a ListPartPics

#convert testa.png -crop 25% %d_test.png 

Max=15
count=-1  

noEntries=16
while [ $count -lt $Max ]  ## ${#ListPartPics[$noEntries]}
      do
	    count=$[$count+1]  # increment the counter by 1
		let "zahl = $RANDOM % $Max + 1" 
		echo "Hier lautet die zahl:" $zahl
		i=0	
		for i in ${ListPartPics[@]}
		do
		echo "This is i:" $i		
	    if [[ $zahl ==  $i ]]
		then  
		count=$[$count-1] # set the counter back to the preceding one
		echo "this is count number in the if loop: set back by 1: " $count
		break 2 					
		fi 					
		done	
ListPartPics[$count]=$zahl 
echo "this is count number" $count
echo ${ListPartPics[$count]}"_test.png" 
done

echo "Anzahl Elemente in der Liste:" ${#ListPartPics[@]}
echo "these are the picture numbers being used:" ${ListPartPics[@]}

Vielen Dank im Voraus für Tipps!

Hannes
 
Hi,

ich hab auch eine Weile gebraucht um dahinter zu steigen was da falsch laeuft, aber vor allem weil der debug output ein wenig schlecht formatiert war, und ich den Algorithmus auch sehr ungewoehnlich finde. ;)

Das Problem ist ganz einfach, dass du nach einem break die letzte Zahl im Array ueberschreibst, weil du nicht checkst ob ein break ausgefuehrt wurde oder nicht. Eine einfache if Abfrage vor der Zuweisung loest das Problem:
PHP:
#!/bin/bash
#Program to crop the picture and reassemble the pieces
declare -a listPartPics

max=15
count=-1

while [ $count -lt $max ]; do

   count=$((count + 1))
   modulo=$((max+1))

   let "number = $RANDOM % $modulo"
   broke=0

   echo -e "\n----new while iteration----"
   echo "number: $number"
   echo '${listPartPics[@]}:' ${listPartPics[@]}

   for i in ${listPartPics[@]}; do
       echo -n "i:" $i ", "
       if [ $number -eq  $i ]; then
           count=$((count - 1))
           broke=1
           break
       fi
   done

   if [ $broke -eq 0 ]; then
       listPartPics[$count]=$number
   fi
   echo -e "\ncount: " $count
   echo ${listPartPics[$count]}"_test.png"
done

echo -e "\nAnzahl Elemente in der Liste:" ${#listPartPics[@]}
echo "These are the picture numbers being used:" ${listPartPics[@]}
Ich guck mal ob ich gleich noch Lust hab das Program etwas leserlicher zu schreiben. In Python saehe eine simple Variante so aus:
PHP:
import random

numbers = []
files = []
max = 15

for i in range(0, max + 1):
    numbers.append(i)

for i in range(0, max + 1):
    number = random.choice(numbers)
    numbers.remove(number)
    files.append(str(number) + "_filename")

print files
Aber das laesst sich leider nicht 1 zu 1 fuer die Bash ubernehmen. ;)

Edit:
Mhh, wer haette gedacht, dass das so schwierig ist. Da die Bash keine Membership Funktionen oder so etwas simples wie array.remove(element) kennt, muss man einige Umwege gehen:
PHP:
#!/bin/bash

# generally, 0 means success, and 1 is an error
# so a return value of 1 means that param 1 ($1)
# does not match any of the following parameters
function isMember
{
    item=$1
    array=( $@ )

    if [ ${#array[@]} -le 1 ]; then
        return 1
    fi

    # delete param 1 from the array
    array[0]=; array=( ${array[@]} )

    # check for membership
    for number in ${array[@]}; do
        if [ $number -eq $item ]; then
           return 0
        fi
    done

    return 1
}

max=15
modulus=$((max + 1))

# fill array $numbers
for ((i = 0; i <= $max; i = i + 1)); do
    numbers[$i]=$i;
done

for ((i = 0; i <= $max; i = i + 1)); do
   # find a number which has not been used yet
   let "rand = $RANDOM % $modulus"
   while isMember $rand ${result[@]}; do
       let "rand = $RANDOM % $modulus"
   done

   result[$i]=$rand

   # remove $rand from $numbers
   for ((j = 0; j < ${#numbers[@]}; j = j + 1)); do
       if [ ${numbers[j]} -eq $rand ]; then
           numbers[j]=; numbers=( ${numbers[@]} )
           break
       fi
   done
done

echo "result: ${result[@]}"
Ob das jetzt wirklich eleganter ist, ist wohl Geschmacksache, das waere jedenfalls mein Ansatz gewesen. ;)

mfg,
bytepool
 
Zuletzt bearbeitet:
Hi bytepool,

vielen Dank für die schnelle Hilfe! :respekt:
weil der debug output ein wenig schlecht formatiert war, und ich den Algorithmus auch sehr ungewoehnlich finde.
Ähm *rotwerd* für mich ist (Shell)Programmierung Neuland und Schwerstarbeit. Ich hab mir überall dort was ausgeben lassen, wo ich dachte, dass es mir bei der Fehlersuche hilft. :hilfe2:
Mich hatte der Gedanke an die Abfrage der breaks gestreift, leider wusste ich das aber nicht umzusetzen.

Aber irgendwie funktioniert es noch nicht ganz. Die Anzahl der Listeneinträge ist 16, aber irgendwie werden Zahlen doppelt ausgegeben. :think:

Ähm, jetzt bist Du mir mit Deinem Edit gerade zuvor gekommen... ;-)
Das teste ich gleich mal.

Vielleicht sollte ich gleich auf Python umsteigen, bevor ich mich weiter in die Bash hineinwühle...
.
.
.
EDIT (autom. Beitragszusammenführung) :
.

perfetto! :respekt:
Es funktioniert ausgezeichnet! Allerdings ist das Skript jetzt schon sehr advanced. Tausend Dank, ich hätte es allein niemals hinbekommen!
 
Zuletzt bearbeitet:
Hi,

wollte nur kurz auch meinen Senf dazugeben ;)

Vorschlag: wenn ein Array so einfach aufgebaut ist (z.B. wie hier nur Zahlen enthält), dann kann man auch mit einer simplen 'case'-Abfrage überprüfen, ob eine bestimmte Zahl darin enthalten ist. Damit wird eine Schleife über die einzelnen Elemente unnötig, und man kann im Fall eines Duplikats mit 'continue' direkt den nächsten Durchlauf der umfassenden 'while'-Loop starten (eine neue Zufallszahl holen), also etwa:

Code:
#!/bin/bash

noEntries=16

count=0
while [ $count -lt $noEntries ]; do
        random=$(($RANDOM % $noEntries))

        case " ${numbers[@]} " in *" ${random} "*) continue ;; esac

        numbers[$count]=${random}
        files[$count]=${random}_test.png

        let count++
done

echo NUMBERS: ${numbers[@]}
echo FILES: ${files[@]}

(NB: Wenn man den Zähler '$count' erst am Ende der Loop hochzählt, also nachdem der $RANDOM-Generator eine _neue_ Zahl geliefert hat, erspart man sich auch das Korrigieren im Fall eines Duplikats.)

Gruss
 
Hi,

Aber irgendwie funktioniert es noch nicht ganz. Die Anzahl der Listeneinträge ist 16, aber irgendwie werden Zahlen doppelt ausgegeben.
Das gepostete Programm funktioniert hier prima. Wenn du die Aenderungen von Hand gemacht hast, hast du sicherlich nur noch was vergessen.

Tausend Dank, ich hätte es allein niemals hinbekommen!
Gern geschehen, aber dein Programm funktionierte ja auch schon fast, fehlten doch nur noch 5 Zeilen, da waerst du sicherlich irgendwann auch noch drauf gekommen. ;)

Allerdings ist das Skript jetzt schon sehr advanced.
Joa, und wie man an floyd's Beispiel sieht, ist die heransgehensweise fuer die Bash auch eher ungeeignet, geht viel einfacher mit deiner Heransgehensweise.
Das war mehr der Versuch den Algorithmus aus Python in die Bash zu uebertragen. Ich hab in der Bash noch nicht viel mit Arrays gemacht, deswgen dachte ich mir, ich guck mir das mal an.

Vielleicht sollte ich gleich auf Python umsteigen, bevor ich mich weiter in die Bash hineinwühle...
Kommt drauf an was du vor hast, hat beides Vor- und Nachteile. Manche Dinge gehen mit einer kompletteren Skriptsprache schneller und leichter, und manche Dinge gehen schneller in der Bash.

@floyd62
Na, das sieht doch schon besser aus, da ist die Einfachheit und Eleganz die ich suchte. ;)
Nebenbei, wenn wir eh schon alle let's eleminieren, "let count++" kann man auch einfach als "((count++))" schreiben.

mfg,
bytepool
 
@bytepool

Merci, das "((count++))" kannte ich noch nicht; man lernt doch nie aus ;)

Grüsse, A.
 
Hallo,

vielen Dank für die viele Mühe!
Und das kurze, knackige Skript von floyd62 ist der Hammer. Ich wär froh, wenn ich sowas auch programmieren könnte. Hab viel dabei gelernt!

Und es auch hübsch angepasst. Nur leider wieder mit einem bug. Es ist zum heulen! X(

Ich poste der Einfachheit halber mal alles (auch wenn das nicht ganz im Sinne der Forumregeln ist :headup:), nicht dass ich versehentlich den Teil mit dem bug auslasse.
PHP:
#!/bin/bash

declare -a eqORgt
eqORgt=("-eq" "-gt");

for k in *.tif; do
		
op=$(($RANDOM % ${#eqORgt[@]}))
noEntries=25
count=0

convert $k -resize 50x35 r${k%.*}.png
convert $ECHO "r${k%.*}.png" -crop 20% %d_mask.png 
while [ $count -lt $noEntries ]; do
        random=$(($RANDOM % $noEntries))

        case " ${numbers[@]} " in *" ${random} "*) continue ;; esac

        numbers[$count]=${random}
        files[$count]=${random}_mask.png
		let Even=$random%2
		if [ $Even ${eqORgt[op]} 0 ] ; then
		convert -rotate 180 $ECHO "${files[$count]}" $ECHO "${files[$count]}"; fi		
		let count++		
done

echo NUMBERS: ${numbers[@]}
echo FILES: ${files[@]}

convert \( $ECHO "${files[0]}" $ECHO "${files[1]}" $ECHO "${files[2]}" $ECHO "${files[3]}" $ECHO "${files[4]}" +append \) \( $ECHO "${files[5]}" $ECHO "${files[6]}" $ECHO "${files[7]}" $ECHO "${files[8]}" $ECHO "${files[9]}" +append \) \( $ECHO "${files[10]}" $ECHO "${files[11]}" $ECHO "${files[12]}" $ECHO "${files[13]}" $ECHO "${files[14]}" +append \) \( $ECHO "${files[15]}" $ECHO "${files[16]}" $ECHO "${files[17]}" $ECHO "${files[18]}" $ECHO "${files[19]}" +append \) \( $ECHO "${files[20]}" $ECHO "${files[21]}" $ECHO "${files[22]}" $ECHO "${files[23]}" $ECHO "${files[24]}" +append \) -append r${k%.*}_mask.png 

rm $ECHO ${files[@]}
mv $ECHO "r${k%.*}_mask.png" PicturesResized/.
mv $ECHO "r${k%.*}.png" PicturesResized/.
done

Für das erste Bild wird das alles hübsch gemacht, aber dann scheint sich das Programm aufzuhängen. Ich habe nirgendwo einen Hinweis gefunden, dass man in eine for- Schleife keine while- Schleife einbauen kann. Das wär jetzt meine Vermutung für den bug gewesen. ?(
Und da das Programm für das erste Bild im Ordner ohne Fehlermeldung durchläuft, frage ich mich, wo der Aufhänger sein soll.

Habt ihr noch einen heißen Tipp?

Tausend Dank für die Hilfe!!!

Schöne Grüße,
Hannes
 
Zuletzt bearbeitet:
Hi,

versuch mal, ob es ausreicht unter der Zeile 10(?)
Code:
        count=0
noch die Zeilen
Code:
        numbers=()
        files=()
zu ergänzen ... sieht für mich so aus, als ob der nach dem ersten Durchlauf die numbers und files Arrays nicht leermacht, und dann hängt das Skript natürlich beim Erzeugen der neuen Zufalls-Folge, weil er ja alle Zahlen schon mal gesehen hat ...

Gruss,
A.
 
Hallo,

versuch mal, ob es ausreicht...

Ähm, ja. Das reicht aus. :D
Darauf hätte ich natürlich auch kommen können... den Zähler hab ich ja immerhin brav auf Null gesetzt.

Vielen Dank nochmals!


Schöne Grüße,
Hannes
 

Ähnliche Themen

Port generieren, wenn nicht dann

X startet nichtmehr

Samba 4 Gast Zugang unter Ubuntu funktioniert nicht

Akonadi startet nicht mehr

NagiosGrapher 1.7.1 funktioniert nicht

Zurück
Oben