0 votes
in SoSci Survey (dt.) by s102724 (110 points)

Liebes SoSciSurvey-Team,

ich führe eine Satzbewertungsstudie durch, bei der die Teilnehmer auf einer einfachen 7-stufigen Skala ihre Bewertungen abgeben können. Das Design der Studie erfordert eine komplexere Randomisierung, die mit shuffle() nicht zu erreichen ist. Die Bedingungen sind folgende:

  • Keine Lexikalisierungen des gleichen Items hintereinander
  • Keine gleichen Lexikalisierungsarten hintereinander

Die Studie beinhaltet insgesamt 9 Items, die jeweils in einer eigenen Rubrik angelegt sind (IA-II). Die einzelnen Fragen in den Rubriken entsprechen den jeweiligen Lexikalisierungen, wobei die Fragen an Position 1 etc. immer der gleichen Lexikalisierungsart entsprechen. Für die Randomisierung habe ich die Fragen jeweils einer Rubrik in ein Array gespeichert, wie exemplarisch hier zu sehen ist:

$item_01 = array(
    'IA01', 'IA02',
    'IA03', 'IA04',
    'IA05'
    );

Alle dieser Arrays werden wiederum in ein weitere Array gespeichert ($items_combined). Für die Randomisierung habe ich eine Funktion geschrieben, die ein Array mit den randomisierten Fragen zurückgibt und als Argument $items_combined nimmt.

// randomizes stimuli
$stimuli_randomised = randomization_custom($items_combined);
registerVariable($stimuli_randomised);	

Die Funktion lautet folgendermaßen:

function randomization_custom($items_combined)
{
	// sets experiment specific numbers
	$amount_lexicalisations  = 5; // lexicalisations per items
	$amount_stimuli = 45; // overall stimuli
	$amount_items = 9; // overall items (rubrics)
		
	// initialises variables
	$stimuli_combined = array(); // creates array for randomized stimuli
	$last_item_number = -1; // default setting for item number
	$last_lexicalisation_number = -1; // default setting lexicalisation number
	
	$i = 0; // loop variable
	while($i < $amount_stimuli) {
		// random item number selection
		$random_item_number = random(0, ($amount_items - 1)); 
		
		// checks whether item number is different from last item number
		if($random_item_number == $last_item_number) {
			if($random_item_number == ($amount_items - 1)) { // item number equals last index 
				$random_item_number = 0;
			}
			else {
				$random_item_number++;
			}
		}	
		$random_item = $items_combined[$random_item_number]; // saves item array to new variable
		
		// random lexicalisation number selection
		$random_lexicalisation_number = random(0, ($amount_lexicalisations - 1));
		
		// checks whether lexicalisation number is different from last lexicalisation number  
 		if($random_lexicalisation_number == $last_lexicalisation_number) {
 			if($random_lexicalisation_number == ($amount_lexicalisations - 1)) { // lexicalisation number equals last index
 				$random_lexicalisation_number = 0; 
 			}
 			else {
 				$random_lexicalisation_number++;
 			}
 		}
		 
		// checks whether position of lexicalisation number is empty, i.e. used-up
		if ($random_item[$random_lexicalisation_number] == 'N/A') {

			$search = TRUE; // loop variable
			while ($search == TRUE) {
				// checks whether other lexicalisations are available in selected item
				if (exploreItem($random_item, $last_lexicalisation_number, $amount_lexicalisations) == TRUE) {
					$search = FALSE; // end loop
				}
				// no other lexicalisation is available
				else { 
					// changes item number
					if ($random_item_number == ($amount_items - 1)) {
						$random_item_number = 0;
					}
					else {
						$random_item_number++;
					}
					// checks whether new item number is identical to old item number
					if ($random_item_number == $last_item_number) {
						if ($random_item_number == ($amount_items - 1)) {
							$random_item_number = 0;
						}
						else {
							$random_item_number++;
						}	
					}
				}
				$random_item = $items_combined[$random_item_number]; // saves item array for further processing
			}

			// selects alternative lexicalisation
			$search_lexicalisation = TRUE; // loop variable
			$random_lexicalisation_number = 0; // starts searching at beginning of array
			
			// loops through item
			while ($search_lexicalisation == TRUE) {
				if ($random_lexicalisation_number == $last_lexicalisation_number) {
					$random_lexicalisation_number++;
				}
				elseif ($random_item[$random_lexicalisation_number] == 'N/A') {					
					$random_lexicalisation_number++;
				}
				else {
					$search_lexicalisation = FALSE;
				}
			}							
		}
		
		// determines stimulus
		$question = $random_item[$random_lexicalisation_number];
		
		// adds selected stimulus to stimuli array
		array_push($stimuli_combined, $question); 
		
		// erases used-up element from original array
		$items_combined[$random_item_number][$random_lexicalisation_number] = 'N/A';
	
		// saves current item and lexicalisation number
		$last_item_number = $random_item_number; 
		$last_lexicalisation_number = $random_lexicalisation_number; 
		
		// increment loop counter
		$i++; 
	}
	
	return $stimuli_combined;
}

Die in randomization_custom() verwendete Funktion exploreItem() ist folgende:

function exploreItem($random_item, $last_lexicalisation_number, $amount_lexicalisations)
{
	$exploring = FALSE; // sets default value
	
	$j = 0; // loop variable
	while ($j < $amount_lexicalisations) {
		if($j != $last_lexicalisation_number) { // checks whether index of array corresponds to last number of lexicalisation
			if($random_item[$j] != 'N/A') { // sets loop variable true if lexicalisation is not already used-up
				$exploring = TRUE;
				$j = $amount_lexicalisations; // enables early termination of loop
			}
		}
		$j++;
	}
	
	return $exploring;
}

Manchmal erscheint bei der Ausführung folgende Fehlermeldung:
Fatal error: Maximum execution time of 5 seconds exceeded

Ich habe den Code bereits mehrmals auf Fehler überprüft, leider ohne Ergebnis. Zwar werden mehrere while-Schleifen verwendet, aber eine infinite loop scheint nicht möglich. Wenn die Fehlermeldung nicht erscheint, generiert der Code die gewünschte Randomisierung fehlerfrei. Ich habe auch versucht die Zeit zu messen: Wenn alles problemlos läuft, beträgt die Laufzeit deutlich unter einer Sekunde (bswp. 5.299999999997E-5, gemessen mit der Differenz der Werte von microtime() vor und nach der Randomisierung). Die Häufigkeit des Fehlers erscheint mir auch ein wenig von der Tageszeit abhängig zu sein, auch wenn das vielleicht täuschen kann.

Ich weiß absolut nicht weiter und würde mich sehr über einen hilfreichen Hinweis zur Behebung freuen. Vielen Dank!

1 Answer

0 votes
by SoSci Survey (304k points)

aber eine infinite loop scheint nicht möglich

Das wäre auch meine erste Vermutung gewesen, aber auch ich sehe im Code keine entsprechende Möglichkeit. Mir fiel lediglich auf, dass Sie in der zweiten Funktion die folgenden Zeilen

$exploring = TRUE;
$j = $amount_lexicalisations; // enables early termination of loop

Einfach ersetzen könnten durch

return TRUE;

Dennoch sehe ich nicht, wo in diesem Code 5 Sekunden lang gerechnet werden sollte.

Wenn alles problemlos läuft, beträgt die Laufzeit deutlich unter einer Sekunde

Das entspricht auch ungefähr meiner Einschätzung des Rechenaufwands...

Als erste Maßnahme würde ich mal die while entfernen, weil diese anfälliger für unbeabsichtigte infinite loops sind also eine FOR-Schleife. Also anstatt

while($i < $amount_stimuli) {

Einfach

for ($i=0; $i < $amount_stimuli; $i++) {

Entsprechend in der zweiten Schleife. Wie gesagt: Nur zum Aufräumen, ich denke nicht, dass das Problem damit automatisch gelöst wäre.

Auf den zweiten Blick in den Code fielen mir dann folgende Zeilen auf:

$search = TRUE; // loop variable
while ($search == TRUE) {

Meine Vermutung wäre, dass hier die Möglichkeit besteht, dass $search niemals false wird. Das würde für einen Logikfehler sprechen. Ich empfehle Folgendes:

Ersetzen Sie dieses While doch einmal durch eine FOR-Schleife mit z.B. 2000 Durchgängen und einem break, wenn die Abbruchbedingung erfüllt ist (oder fügen Sie einen Zähler ein, der spätestens nach 2000 Versuchen abbricht). Wenn der Timeout damit behoben ist, geht es um die Suche des Logikfehlers. Schreiben Sie dafür vor die Schleife

debug($random_item);
debug($last_lexicalisation_number);
debug($amount_lexicalisations);

... und was immer für den Verlauf der Schleife relevant ist. Und hinter die Schleife natürlich eine Anzeige, Dann sollten Sie schnell sehen, in welchen Konstellationen die 2000 Durchgänge passieren und wann nicht.

by s102724 (110 points)
Vielen Dank für Ihre Hinweise! Ich habe den Code entsprechend Ihrer Vorschläge aufgeräumt. Das Problem verschwand tatsächlich sobald ich eine FOR-Schleife verwendet habe. Es muss, wie sie richtig vermutet haben, Konstellationen geben, in denen $search nicht FALSE werden kann. Meine Kontrollstrukturen sind wohl etwas zu einfach konzipiert..
by SoSci Survey (304k points)
Ja, ich habe auch den Eindruck, dass diese Randomisierung noch viel zu wenig Komplexität hat ;) Aber manchmal geht es eben nicht anders.

Wenn Sie das Ziel der Rotation/Randomisierung einmal in Worte fassen möchten (ich habe nicht versucht, den ganzen Code zu verstehen), habe ich vielleicht eine Idee, wie das Ziel effizienter zu erreichen wäre.

Willkommen im Online-Support von SoSci Survey.

Hier bekommen Sie schnelle und fundierte Antworten von anderen Projektleitern und direkt von SoSci Survey.

→ Eine Frage stellen


Welcome to the SoSci Survey online support.

Simply ask a question to quickly get answers from other professionals, and directly from SoSci Survey.

→ Ask a Question

...