Sign in to follow this  
Followers 0
Kapz

Complex Sort Function - Revised?

19 posts in this topic

#1 ·  Posted (edited)

I added a post about randomizing it on the second page.

I am working on a complex sorting script in my lab and I've been having some trouble trying to figure out how to tackle the program with AutoIt. I basically have a long list of possible combinations (576 elements) that I need to split into 6 smaller lists (96 elements). Each element consists of a short array of 6 different letters. The first half of the array (list element) consists of 3 letters made up of any combination of 4 letters (A,B,C, or D) but none of the letters may repeat in that instance. The second half of the array also consists of 3 letters made up of any combination of 4 other letters (E,F,G, or H) with no repeating letters for that element . Here are some possible examples:

ABCEFG

DBCEFG

CADHEF

If my math is correct, that should make 576 possible combinations assuming order matters (4 letters x 3 letters x 2 letters in the first half multiplied by 4 letters x 3 letters x 2 letters in the second half). The sort function will start out with a long list of 576 of these unique elements which is will try to split up into 6 smaller lists consisting of 96 elements. Here's the complex sort part. The smaller lists need to be counterbalanced so that there are an equal number of A's, B's, C's, D's, E's, F's, G's, and H's in them. I already know every possible combination and have written all 576 of them out in Excel, I just need to find a way to have a fancy sort script split it up so everything is counterbalanced. I have already written it out by hand using 3 possible letters rather than 4 for 4 spots (a 4 character array for each element) rather than 6 so I know its doable. Any help would be greatly appreciated!

Edited by Kapz

Share this post


Link to post
Share on other sites



Easy:

Do

PuzzledLook()

Until $forever

Share this post


Link to post
Share on other sites

Okay I think you are using the term "array" too loosely.

Each element consists of a short array of 6 different letters.

I'm assuming you mean "string" and not an actual array???

The first half of the array (list element) consists of 3 letters made up of any combination of 4 letters (A,B,C, or D) but none of the letters may repeat in that instance. The second half of the array also consists of 3 letters made up of any combination of 4 other letters (E,F,G, or H) with no repeating letters for that element .

Again when you say "first half of the array" are you meaning first half of the string?

Share this post


Link to post
Share on other sites

#5 ·  Posted (edited)

This script generated the combinations that is talked about.

They need to be split into 6 stacks of 96 record in which each letter appear 72 timers ... :)

FileDelete("test.txt")
; each char (8) needs to be in each file(6) 72 times
Dim $Counters[8][6]
Dim $A1[4] = ["A", "B", "C", "D"]
Dim $A2[4] = ["E", "F", "G", "H"]
$count = 0
For $x1 = 0 To 3
    For $x2 = 0 To 3
        For $x3 = 0 To 3
            For $y1 = 0 To 3
                For $y2 = 0 To 3
                    For $y3 = 0 To 3
                ; Ensure different characters
                        If $x1 = $x2 Or $x1 = $x3 Or $x2 = $x3 Then ContinueLoop
                        If $y1 = $y2 Or $y1 = $y3 Or $y2 = $y3 Then ContinueLoop
                ; Check the counters to ensure that each char is only 72 times in each file
                        FileWriteLine("test.txt", $A1[$x1] & $A1[$x2] & $A1[$x3] & $A2[$y1] & $A2[$y2] & $A2[$y3])
                    Next
                Next
            Next
        Next
    Next
Next
Edited by Jos

Visit the SciTE4AutoIt3 Download page for the latest versions  - Beta files                                How to post scriptsource        Forum Rules
 
Live for the present,
Dream of the future,
Learn from the past.
  :)

Share this post


Link to post
Share on other sites

#6 ·  Posted (edited)

Wow.

... (eyes begin to refocus) OK, I dig the concept of this one. I've got a few process flow thoughts running in my head now on how to pull it off, I'll post whatever snippets I come up with.

Edit: lol, nm - apparently in the time it took me to read the description, Jos tore right through the issue (and much simpler, cleaner code than I would have had).

Edited by Monamo

- MoChr(77)& Chr(97)& Chr(100)& Chr(101)& Chr(32)& Chr(121)& Chr(97)& Chr(32)& Chr(108)& Chr(111)& Chr(111)& Chr(107)-------I've told you 100,000 times not to exaggerate!-------Don't make me hit you with my cigarette hand...-------My scripts:Random Episode Selector, Keyboard MouseMover, CopyPath v2.1, SmartRename for XP,Window Tracer[sup]New![/sup]

Share this post


Link to post
Share on other sites

I was on the right track too until Jos posted so I will try to work with his version. There are question marks floating over my head.

Share this post


Link to post
Share on other sites

#8 ·  Posted (edited)

Do I get a prize?

EDIT: All fixed now

#include <array.au3>

Dim $masterArray[1000][2]

$size = 96

Dim $subArray1[$size]
Dim $subArray2[$size]
Dim $subArray3[$size]
Dim $subArray4[$size]
Dim $subArray5[$size]
Dim $subArray6[$size]

; each char (8) needs to be in each file(6) 72 times
Dim $A1[4] = ["A", "B", "C", "D"]
Dim $A2[4] = ["E", "F", "G", "H"]
$count = 0
For $x1 = 0 To 3
    For $x2 = 0 To 3
        For $x3 = 0 To 3
            For $y1 = 0 To 3
                For $y2 = 0 To 3
                    For $y3 = 0 To 3
                        ; Ensure different characters
                        If $x1 = $x2 Or $x1 = $x3 Or $x2 = $x3 Then ContinueLoop
                        If $y1 = $y2 Or $y1 = $y3 Or $y2 = $y3 Then ContinueLoop
                        
                        ; Check the counters to ensure that each char is only 72 times in each file
                        $masterArray[$count][0] = $A1[$x1] & $A1[$x2] & $A1[$x3] & $A2[$y1] & $A2[$y2] & $A2[$y3]
                        ;$masterArray[$count][1] = Asc($A1[$x1]) + Asc($A1[$x2]) + Asc($A1[$x3]) + Asc($A2[$y1]) + Asc($A2[$y2]) + Asc($A2[$y3])
                        $masterArray[$count][1] = GenerateChecksum($masterArray[$count][0])
                        $count += 1
                    Next
                Next
            Next
        Next
    Next
Next

Redim $masterArray[$count][2]

_ArraySort ($masterArray, 0,0,0,2,1)

;_Arraydisplay($masterArray)

$X = 0
For $multiplier = 0 to 15
    For $Y = 1 to 6
        ConsoleWrite("::::::::::::::::::::::::::::::::::::::::::::::ARRAY " & $Y & @CRLF & @CRLF)
        For $Z = 1 to 6
            $element = $Z + ($multiplier * 6) -1
            
            Switch $Y
                Case 1
                    $subArray1[$element] = $masterArray[$X][0]
                Case 2
                    $subArray2[$element] = $masterArray[$X][0]
                Case 3
                    $subArray3[$element] = $masterArray[$X][0]
                Case 4
                    $subArray4[$element] = $masterArray[$X][0]
                Case 5
                    $subArray5[$element] = $masterArray[$X][0]
                Case 6
                    $subArray6[$element] = $masterArray[$X][0]                
            EndSwitch
            ConsoleWrite("Y: " & $Y & " Z: " & $Z & " X: " & $X & @CRLF)
            ConsoleWrite("Element: " & $element & @CRLF)
            $X += 1
        Next
    Next
Next

VerifyCounts($subArray1)

;_Arraydisplay($subArray1)
;_Arraydisplay($subArray2)
;exit
;_Arraydisplay($subArray3)
;_Arraydisplay($subArray4)
;_Arraydisplay($subArray5)
;_Arraydisplay($subArray6)

Func VerifyCounts($theArray)

    $dictionaryObj = ObjCreate("Scripting.Dictionary")
    For $X = 0 to Ubound($theArray) - 1
        $tempArray = StringSplit($theArray[$X], "")
        For $Y = 1 to $tempArray[0]
            $dictionaryObj.Item($tempArray[$Y]) = $dictionaryObj.Item($tempArray[$Y]) + 1
        Next
    Next
    
    #include <GUIConstants.au3>

    GUICreate("Character Occurrences", 200)  ; will create a dialog box that when displayed is centered
    GUISetState (@SW_SHOW)       ; will display an empty dialog box

    $listview = GUICtrlCreateListView ("Character | Occurrences",10,10,180,380,$LVS_SORTASCENDING)
    For $key In $dictionaryObj.Keys()
        GUICtrlCreateListViewItem($key & "|" & $dictionaryObj.Item($key),$listview)
    Next
    
    ; Run the GUI until the dialog is closed
    While 1
        $msg = GUIGetMsg()
        If $msg = $GUI_EVENT_CLOSE Then ExitLoop
    Wend
EndFunc

Func GenerateChecksum($theString)
    $checksum = 0
    $tempArray = StringSplit($theString, "")
    For $X = 1 to $tempArray[0]
        Switch $tempArray[$X]
            Case "A"
                $checksum += 2
            Case "B"
                $checksum += 4
            Case "C"
                $checksum += 8
            Case "D"
                $checksum += 16
            Case "E"
                $checksum += 32
            Case "F"
                $checksum += 64
            Case "G"
                $checksum += 128
        EndSwitch
    Next
    
    Return $checksum
EndFunc

Explanation:

1. All possible strings are placed into a 2 dimensional array. Dimension 1 is the string itself and dimension 2 is a "checksum" of the string which should always be the same if you were to shuffle the string.

2. The array is sorted by the checksum, which effectively gives you 16 different checksums times 36 elements. To distribute an equal amount of elements from each checksum we will form the 6 arrays by taking 6 elements from each checksum "group".

3. A loop is run through the master array in 16 sections, each section being a unique checksum. Each section will be read in sub-sections of 36 elements and said elements are copied into their respective array.

Edited by weaponx

Share this post


Link to post
Share on other sites

Think this one is what is wanted..... was a nice brain buster :)

FileDelete("Segment0.txt")
FileDelete("Segment1.txt")
FileDelete("Segment2.txt")
FileDelete("Segment3.txt")
FileDelete("Segment4.txt")
FileDelete("Segment5.txt")
; each char (8) needs to be in each file(6) 72 time
Dim $Counters[8][6]
Dim $A1[4] = ["A", "B", "C", "D"]
Dim $A2[4] = ["E", "F", "G", "H"]
$count = 0
For $x1 = 0 To 3
    For $x2 = 0 To 3
        For $x3 = 0 To 3
            For $y1 = 0 To 3
                For $y2 = 0 To 3
                    For $y3 = 0 To 3
                    ; Ensure different characters
                        If $x1 = $x2 Or $x1 = $x3 Or $x2 = $x3 Then ContinueLoop
                        If $y1 = $y2 Or $y1 = $y3 Or $y2 = $y3 Then ContinueLoop
                    ; Check the counters to ensure that each char is only 72 times in each file
                        $LowestNr = 9999
                        $LowestFl = 6
                        $sf = 0
                        Dim $Full[6] =[0,0,0,0,0,0]     ; keep track of full tables
                        Dim $Lowest[6] =[99,99,99,99,99,99]; Find lowest count in tables
                        Dim $Total[6] =[0,0,0,0,0,0]       ; Find total counts of the whole table
                        For $f = 0 To 5
                            For $c = 0 To 7
                                If $c < 4 Then $tc = $A1[$c]
                                If $c > 3 Then $tc = $A2[$c - 4]
                                If StringInStr($A1[$x1] & $A1[$x2] & $A1[$x3] & $A2[$y1] & $A2[$y2] & $A2[$y3], $tc) Then
                                    $Total[$f] += $Counters[$c][$f]
                                ; Found full table ?
                                    If $Counters[$c][$f] > 71 Then 
                                        $full[$f] = 1
                                        ContinueLoop 2
                                    EndIf
                                ; tack lowest count
                                    If $Counters[$c][$f] < $Lowest[$f] Then $Lowest[$f] = number($Counters[$c][$f])
                                EndIf
                            Next
                        Next
                    ; store result in table with lowest count and lowest total count
                        $lows = 99
                        $LowestFl = 6
                        $Totals = 999
                        For $x = 0 to 5
                            If $full[$x] <> 1 and $Lowest[$x] <= $lows and $Totals > $Total[$x] then 
                                $lows = $Lowest[$x]
                                $Totals = $Total[$x]
                                $LowestFl = $x
                            EndIf
                        Next    
                        If $LowestFl < 6 Then
                        ; Update the counters of the found table
                            For $c = 0 To 7
                                If $c < 4 Then $tc = $A1[$c]
                                If $c > 3 Then $tc = $A2[$c - 4]
                                If StringInStr($A1[$x1] & $A1[$x2] & $A1[$x3] & $A2[$y1] & $A2[$y2] & $A2[$y3], $tc) Then
                                    $Counters[$c][$LowestFl] += 1
                                EndIf
                            Next
                            FileWriteLine("Segment" & $LowestFl & ".txt", $A1[$x1] & $A1[$x2] & $A1[$x3] & $A2[$y1] & $A2[$y2] & $A2[$y3])
                        Else
                            ConsoleWrite("Error not able to assign this combo:" & $A1[$x1] & $A1[$x2] & $A1[$x3] & $A2[$y1] & $A2[$y2] & $A2[$y3])
                        EndIf
                    Next
                Next
            Next
        Next
    Next
Next
Consolewrite("###################################" & @CRLF)
Consolewrite("### Checkking the counts table ####" & @CRLF)
Consolewrite("###################################" & @CRLF)
For $f = 0 To 5
    For $c = 0 To 7
        ConsoleWrite($Counters[$c][$f] & " ")
    Next
    ConsoleWrite(@CRLF)
Next

Visit the SciTE4AutoIt3 Download page for the latest versions  - Beta files                                How to post scriptsource        Forum Rules
 
Live for the present,
Dream of the future,
Learn from the past.
  :)

Share this post


Link to post
Share on other sites

Did you check the count in each Array for A,B,C,D,E and F ? Don't think they are 72 in each array . :)

I see what you mean. I guess I assumed there was a balanced number of each checksum after the sort (96 each times 6). I will try to fix it.

Share this post


Link to post
Share on other sites

The script I posted is all good now. 72 instances of each character in each array.

Share this post


Link to post
Share on other sites

Wow, that is amazing! I thought about different ways to approach the problem and I even wrote it out by hand to make sure it was doable using 3 letters for both halves (A,B,C and E,F,G) instead of 4 letters with only 4 possible spots instead of 6 in the string....and that worked. When I tried to use a 6 letter string with 4 possible characters for each half, it became much mroe complicated. I will test the code today, but thank you so very much in advance, you guys are freaken awesome!

Share this post


Link to post
Share on other sites

Thank you so very much for your help Jos and everyone else, the script worked perfectly! Now, on to fun experiments in the lab!

Share this post


Link to post
Share on other sites

This may seem very simple but I was wondering if the script could be modified to randomize what each of the 6 groups consists of? For example, the first group may have something like ABCEFG but that will never exist in the second group unless the program can be randomized to move things around into different groups. I know this is possible, I'm just not sure of an easy way to accomplish it with the script that Jos came up with? Maybe if the original list that is created is randomised first and then sorted into 6 groups? Thanks again for all of yalls help!

Share this post


Link to post
Share on other sites

I'm not really sure about what you are wanting. We have 2 pools of characters from which strings are formed, ABCD and EFGH. Are you saying you want to scramble those so it can be like DEAH BGCF???

Share this post


Link to post
Share on other sites

I'm not really sure about what you are wanting. We have 2 pools of characters from which strings are formed, ABCD and EFGH. Are you saying you want to scramble those so it can be like DEAH BGCF???

Let me provide a brief example of what I'm referring to. So below are two of the 6 groups with only a few items instead of 96:

ABCEFG

BDAEFG

BCAHGE

ABCFEG

CDBHEF

ADCGEH

So any one of the three combinations in the first group will never exist in the second group (ex: ABCEFG) since everything has to be counter-balanced within that group (same number of A's, same number of G's ,etc). However, you could switch up the groups by swapping the first element (ABCEFG) in group one with the first element in group 2 (ABCFEG) since they have the exact same element, just a different order. This will allow for the 6 groups to exist with everything counterbalanced within the groups. I'm just not sure how to accomplish this with the script. Does it make more sense to do this as it creates the list or would it be better to create another script that can go through and swap items out, further randomizing the lists within each of the 6 groups (of 96 elements)?

Thanks!

Share this post


Link to post
Share on other sites

Here is the script that I came up with to accomplish what I was looking for, maybe the code can explain it better than I can with words. Sorry if my code is a bit rusty, I'm sure there is a prettier and more efficient way to accomplish this. Thanks!

#include <File.au3>
FileOpen("Segment0.txt", 1)
FileOpen("Segment1.txt", 1)
FileOpen("Segment2.txt", 1)
FileOpen("Segment3.txt", 1)
FileOpen("Segment4.txt", 1)
FileOpen("Segment5.txt", 1)

;randomization loop max
$max = 1000

;counter of voided loops ($max - $s1 = true number of loops)
$s1 = 0

;Loop to call swap function with random list picking
For $count = 1 to $max
    $t1 = int(random(0,5))
    $t2 = int(random(0,5))
    If $t1 = $t2 Then
        $s1 = $s1 + 1
    Else    
        Swap("Segment" & $t1 & ".txt","Segment" & $t2 & ".txt")
    EndIf
Next

;Close all files when done
FileClose("Segment0.txt")
FileClose("Segment1.txt")
FileClose("Segment2.txt")
FileClose("Segment3.txt")
FileClose("Segment4.txt")
FileClose("Segment5.txt")

;Check to make sure that there are 72 occurences of each unique letter within each txt file
check("Segment0.txt")
check("Segment1.txt")
check("Segment2.txt")
check("Segment3.txt")
check("Segment4.txt")
check("Segment5.txt")

;Write out the number of voided loops
ConsoleWrite($s1)

;Swap function to swap out one element in a txt file with a similiar element in another txt file
Func Swap($list1, $list2)
    $stop = 0
    
    For $d = 1 to 96
        $rand2 = int(Random(1,96))
        $y1 = FileReadLine($list2, $rand2)
        
        For $c = 1 to 96
            $rand = int(Random(1,96))
            $x1 = FileReadLine($list1, $rand)
            $num = StringRegExp($x1, StringLeft($y1,1))
            $num = $num + StringRegExp($x1, StringRight(StringLeft($y1,2),1))
            $num = $num + StringRegExp($x1, StringRight(StringLeft($y1,3),1))
            $num = $num + StringRegExp($x1, StringRight(StringLeft($y1,4),1))
            $num = $num + StringRegExp($x1, StringRight(StringLeft($y1,5),1))
            $num = $num + StringRegExp($x1, StringRight(StringLeft($y1,6),1))
            
        ;If match has identical letters but not necessarily in the same order then replace and exit loop
            If $num = 6 Then
                _FileWriteToLine ($list1, $rand, $y1 ,1)
                _FileWriteToLine ($list2, $rand2, $x1 ,1)
                $stop = $stop + 1
                ExitLoop
            EndIF
            
            $num = 0
        Next
        
        If $stop = 1 Then
            ExitLoop
        EndIf
        
    Next
    
EndFunc


Func Check($checklist)
    $A = 0
    $B = 0
    $C = 0
    $D = 0
    $E = 0
    $F = 0
    $G = 0
    $H = 0  
    For $t = 1 to 96
        If StringRegExp(FileReadLine($checklist,$t), "A") = 1 Then
            $A = $A + 1
        EndIf
        If StringRegExp(FileReadLine($checklist,$t), "B") = 1 Then
            $B = $B + 1 
        EndIf           
        If StringRegExp(FileReadLine($checklist,$t), "C") = 1 Then
            $C = $C + 1     
        EndIf
        If StringRegExp(FileReadLine($checklist,$t), "D") = 1 Then
            $D = $D + 1
        EndIf
        If StringRegExp(FileReadLine($checklist,$t), "E") = 1 Then
            $E = $E + 1
        EndIf
        If StringRegExp(FileReadLine($checklist,$t), "F") = 1 Then
            $F = $F + 1 
        EndIf
        If StringRegExp(FileReadLine($checklist,$t), "G") = 1 Then
            $G = $G + 1
        EndIf
        If StringRegExp(FileReadLine($checklist,$t), "H") = 1 Then
            $H = $H + 1
        EndIf
    Next
    ConsoleWrite("A=" & $A & " | B=" & $B & " | C=" & $C & " | D=" & $D & " | E=" & $E & " | F=" & $F & " | G=" & $G & " | H=" & $H & @CRLF)
EndFunc

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0