4

This is the imageI've the following five arrays

var E1 = ["A", "B", "C", "D", "E"]
var E2 = ["A", "B", "C", "D", "E"]
var E3 = ["A", "B", "C", "D", "E"]
var E4 = ["A", "B", "C", "D", "E"]
var E5 = ["A", "B", "C", "D", "E"]

Each array have the same five elements namely "A", "B", "C", "D" and "E". I want to write an algorithm to sort the elements in all the arrays such that no two arrays have the same element (let's say "A") at the same index.

A sort of the sample output that will work for me will be like:

var E1 = ["A", "B", "C", "D", "E"]
var E2 = ["B", "C", "D", "E", "A"]
var E3 = ["C", "D", "E", "A", "B"]
var E4 = ["D", "E", "A", "B", "C"]
var E5 = ["E", "A", "B", "C", "D"]

I've tried to solve this but couldn't complete. I've just written a shuffling function for sorting the elements of two arrays(E1 and E2).

var E1 = ["A", "B", "C", "D", "E"]
var E2 = ["A", "B", "C", "D", "E"]
var E3 = ["A", "B", "C", "D", "E"]
var E4 = ["A", "B", "C", "D", "E"]
var E5 = ["A", "B", "C", "D", "E"]

func shuffledArrays(var array1: [String],var array2: [String]) {


if array1[0] == array2[0] || array1[1] == array2[1] || array1[2] == array2[2] || array1[3] == array2[3] || array1[4] == array2[4] {

    shuffled1 = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(array1)
    shuffled2 = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(array2)

    var array3 = shuffled1 as! [String]
    var array4 = shuffled2 as! [String]

} else {
    var array3 = array1
    var array4 = array2
}

array1 = array3
    array2 = array4
}

// Now calling the function on arrays E1 and E2
    shuffledArrays(E1, array2: E2)
    print(E1)
    print(E2)

With this code I'm getting the following error on Xcode Playground. While sometimes the error is removed and the output is correct at lines 102 and 103 but still I'm unable to extract that output out and save it permanently into E1 and E2 respectively. Please help me with the whole algorithm in arranging the five arrays' elements.

Thanks

Faraz Ahmad
  • 515
  • 1
  • 6
  • 13
  • 1
    What programming language, son? – Phiter Dec 25 '15 at 17:19
  • 1
    Does it need to be random? Or do all unique elements need to have different indexes in different arrays? It's just a bit counter productive to do both. If you need both, please add some context. – R Menke Dec 25 '15 at 17:25
  • @RMenke it seems that he needs five random different permutations of the array ... – user3441734 Dec 25 '15 at 17:31
  • 1
    @user3441734 he clearly stated that no two arrays should have the same element at the same index. Which is not random at all. – R Menke Dec 25 '15 at 17:34
  • @RMenke i agree, see the answer written by Leo. – user3441734 Dec 25 '15 at 17:35
  • @user3441734 it does not answer the question, that just returns random arrays. – R Menke Dec 25 '15 at 17:36
  • @RMenke he takes 5 different permutations in the set. his answer looks right to me. – user3441734 Dec 25 '15 at 17:38
  • @PhiterFernandes, I'm working on swift sir. – Faraz Ahmad Dec 25 '15 at 17:38
  • @RMenke, yes it can/cannot be random. The sample output I've included in the question is just an example. However, in real actually the elements of the arrays are not similar strings (that's some arrays may have string-elements "F", "G" etc. in place of "A", "B" etc.). – Faraz Ahmad Dec 25 '15 at 18:28
  • You write: "I want to write an algorithm to sort the elements in all the arrays such that no two arrays have the same element (let's say "A") at the same index." But is it ok that other elements can have the same index? Or should just "A" have another index in all the arrays? – Darko Dec 26 '15 at 06:27
  • @FarazAhmad take a look at my answer. I think it will do everything you need. It mutates the arrays until no two equal elements have the same index. – R Menke Dec 26 '15 at 16:27
  • @Darko, No sir. No two arrays should have "ANY" same element at the same index. For example if "A" occurs at the index0 of array1, then it cannot occur at index0 of any other array. Similarly if "B" occurs at index1 of array1, then it cannot occur at index1 of any other array. – Faraz Ahmad Dec 26 '15 at 18:15
  • @RMenke, thanks for your effort. Yup, it seems to be working well. I've posted the actual problem (in the image) and I'll test it if it works on the actual problem and I'll update here. Thanks very much again. – Faraz Ahmad Dec 26 '15 at 18:21
  • @FarazAhmad it no longer cares if the arrays are of equal length. The gist with the Matrix type has been updated. It will now also return `nil` where there is no possible solution – R Menke Dec 26 '15 at 22:14

5 Answers5

7

Since you know arrays E1, ..., E5 to hold identical entries (in identical order), you needn't explicitly hold the arrays E2 through E5 (since you know these are value-equal to E1).

Hence, you could simply define a shift function and create E2 through E5 by repeated shifting of previous array.

import GameplayKit

func shiftByOne (arr: [String]) -> [String] {
    var shiftedArr = arr
    shiftedArr.insert(shiftedArr.popLast()!, atIndex: 0)
    return shiftedArr
}

var E1 = ["A", "B", "C", "D", "E"]
E1 = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(E1) as! [String]
var E2 = shiftByOne(E1)
var E3 = shiftByOne(E2)
var E4 = shiftByOne(E3)
var E5 = shiftByOne(E4)

/** Result without initial shuffle:
E1 = ["A", "B", "C", "D", "E"]
E2 = ["B", "C", "D", "E", "A"]
E3 = ["C", "D", "E", "A", "B"]
E4 = ["D", "E", "A", "B", "C"]
E5 = ["E", "A", "B", "C", "D"] */

This method starts with E1 (possibly shuffling only this array), and guarantees that E2 through E5 are constructed to all differ, w.r.t. order, from each other.

As noted by R Menke below, if you shuffle array E1, the same behaviour will hold (however with shuffled initial array). Here I've used the same shuffler as in your example, but for a more Swifty approach, see e.g.:

Community
  • 1
  • 1
dfrib
  • 70,367
  • 12
  • 127
  • 192
  • if you randomise the array before starting the shift it will do all that the OP asked for. If you randomise the order of the arrays E1 - E5, it will be even better. – R Menke Dec 25 '15 at 17:39
  • guys, even you randomise the array before shifting, that is not enough to have random subset of all possible permutations. see Leos answer, it is closest to give the guy the good results – user3441734 Dec 25 '15 at 17:53
  • 2
    @user3441734 but the OP doesn't want completely random arrays. He wants each element to have a different index in respect to the other arrays. – R Menke Dec 25 '15 at 18:08
  • @dfri, Thanks dfri for the answer. It does serve the purpose of my sample output. But sometimes in the future, I want to have some specified elements of some of the arrays that I don't want to shift and also there is even a case when elements of the arrays are different strings. Could you please suggest how to modify accordingly? – Faraz Ahmad Dec 25 '15 at 18:26
  • @dfri, to explain this clearly I need to upload an image. I'm new programmer and new to the stackoverflow as well (for posting questions) and I don't know how to upload images privately specifically to you. Could you please guide me how I can do that? – Faraz Ahmad Dec 25 '15 at 18:48
  • @dfri, thanks for the suggestion. But I don't want to post that image/photo publicly. That picture depicts the actual application to the problem I've asked here. I don't know about the stackoverflow rules, so asking may we chat on skype if it is feasible for you and allowed by stackoverflow? Otherwise, I will try to explain in other words (but actual picture will do the best). Thanks – Faraz Ahmad Dec 25 '15 at 18:55
  • @dfri, thanks for the help. I've read your messages. I'm preparing an alternate image/file that will depict like the actual image. I'm uploading that alternate image in 3-4 minutes. Thanks. – Faraz Ahmad Dec 25 '15 at 19:09
  • @dfri, I've added the image file. Please look at it. It has five rows namely Location1 through Location5. It has fourteen columns namely S1 through S14. – Faraz Ahmad Dec 25 '15 at 19:19
  • @FarazAhmad Lets take this in chat: http://chat.stackoverflow.com/rooms/98985/faraz – dfrib Dec 25 '15 at 19:25
  • @dfri, In the column S14, I've highlighted its two elements "Frz" and "Jahn" at locations 1 and 5 respectively. I want to shuffle these with each other, that's "Jahn" to be at Location1 and "Frz" to be at Location5. But "Jahn" cannot go to Location1 because it already exists at Location1 in the column S3 (also highlighted). – Faraz Ahmad Dec 25 '15 at 19:26
  • In order to succeed the shuffling as described, "Jahn" of S3 should shuffle with "AamZk" at Location5 of the column S3. But again "AamZk cannot be placed at Location1 unless it is moved to somewhere else from Location1 of the column S10 (probably in place of "Frn" at Location5 which is possible because "Frn" does not exist anywhere else at Location5 of any other column. But the problem is that "Frn" cannot move either, because it can only be at Location4 or Location3. Not anywhere else. So this series continues, and I want to solve this issue. – Faraz Ahmad Dec 25 '15 at 19:27
2

As far as I can see you can make it this way:

  1. Take a valid solution (just like the example you gave)
  2. shuffle the columns (this can only lead to valid solutions)
  3. shuffle the rows (this can only lead to valid solutions)

This way you can get all possible solutions.

For example, starting with a valid solution.

["A", "B", "C", "D", "E"]
["B", "C", "D", "E", "A"]
["C", "D", "E", "A", "B"]
["D", "E", "A", "B", "C"]
["E", "A", "B", "C", "D"]

Shuffle the rows.

["C", "D", "E", "A", "B"]
["B", "C", "D", "E", "A"]
["A", "B", "C", "D", "E"]
["D", "E", "A", "B", "C"]
["E", "A", "B", "C", "D"]

Then shuffle the columns.

["C", "E", "A", "D", "B"]
["B", "D", "E", "C", "A"]
["A", "C", "D", "B", "E"]
["D", "A", "B", "E", "C"]
["E", "B", "C", "A", "D"]

This guarantees no two arrays have the same element in the same index and they're randomized.

Here is an example in Ruby. The algorithm is applicable to any language.

# Initialize the first row of the matrix
matrix = []
matrix[0] = ('A'..'E').to_a

size = matrix[0].size

# Initialize the rotated array
for i in 1..size-1
  matrix[i] = matrix[i-1].rotate
end

puts "Original matrix"
for x in matrix
  puts x.inspect
end

# Shuffle the indexes of the rows and columns
rows = (0..size-1).to_a.shuffle
cols = (0..size-1).to_a.shuffle

# Shuffle the rows
for i in 0..size-1
  row1 = i
  row2 = rows[i]
  tmp = matrix[row1]
  matrix[row1] = matrix[row2]
  matrix[row2] = tmp
end

# Shuffle the columns.
for i in 0..size-1
  col1 = i
  col2 = cols[i]

  for j in 0..size-1
    tmp = matrix[j][col1]
    matrix[j][col1] = matrix[j][col2]
    matrix[j][col2] = tmp
  end
end

puts "Shuffled matrix"
for x in matrix
  puts x.inspect
end

@Schwern: Tanks for adding the example and the code.

MrSmith42
  • 9,961
  • 6
  • 38
  • 49
  • 4
    Can you prove this method can yield all solutions? – Tamas Hegedus Dec 25 '15 at 17:46
  • This won't be valid. If you shuffle a row, there's nothing that stops it from matching another row. – Schwern Dec 25 '15 at 19:42
  • @Schwern: The initial solution was valid, whatever rows we swap it keeps to be a valid solution. Same for columns. If you spat two columns the solution stays valid. – MrSmith42 Dec 25 '15 at 22:40
  • 1
    @MrSmith I think I understand what you mean now, and you're right. You mean to shuffle the list of rows and columns, not their content. – Schwern Dec 25 '15 at 23:01
  • @MrSmith42 I hope my edit wasn't too much, but I felt you should get credit for the correct algorithm. – Schwern Dec 25 '15 at 23:31
0

i like Leo's answer. My answer is almost 'pure' swift (import Foundation is there due to arc4random_uniform function)

import Foundation // arc4random_uniform

let arr = Array(1...5)
var result: [[Int]] = []
repeat {
    let r = arr.sort { (a, b) -> Bool in
        arc4random_uniform(2) == 0
    }
    if !result.contains({ (s) -> Bool in
        s == r
    }) {
        result.append(r)
    }
} while result.count < 6

print(result)
// [[2, 3, 1, 4, 5], [4, 2, 1, 3, 5], [2, 1, 3, 4, 5], [2, 3, 1, 5, 4], [2, 4, 5, 1, 3], [2, 1, 4, 3, 5]]
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
user3441734
  • 16,722
  • 2
  • 40
  • 59
  • Note that for Leos answer: that there would, on avarage, be a huge overset for his method. Try analyze the expected number of runs within the while loop: randomizing 5 arrays of size 5 all to distinctly differ from eachother. The number of quintuple array permutations that **do not** satisfy this is huge. Your method above, however, looks less wasteful. – dfrib Dec 25 '15 at 18:21
  • @dfri, check it, you will see that it is the same effectivity, as in Leo's answer. if you do some performance analysis, 'pure' swift will not be worst as Leo's solution. lets try it! – user3441734 Dec 25 '15 at 18:25
  • 1
    @LeoDabus :-) so, maybe someone else can use our solutions. Yes, i see it now! – user3441734 Dec 25 '15 at 18:28
  • @LeoDabus, No the order can be any different from the one I provided in the sample output. The problem with user3441734's code is the same. The output I got with this code still has the element '2' at the index0 of two arrays. I want to have a single element at different indexes of the different arrays. – Faraz Ahmad Dec 25 '15 at 18:39
  • @user3441734: Note that I write that you method above is probably fine (_"Your method above, however, looks less wasteful"_, perhaps a bad choice of words). I was only concerned with Leos answer, but I can't test it now as it's been removed. But maybe, as I glanced over it, I misunderstood what happened in the while loop. I interpreted the while loop as _"brute force trying to generate random arrays until they are unique"_, but maybe I misunderstood. – dfrib Dec 25 '15 at 18:45
  • @LeoDabus I've seen it, thank! (Note however the the sets are still not "unique" with regard to the question specifications) – dfrib Dec 25 '15 at 19:49
0

I am answering from an iPhone, so I have no compiler to test some code. I can just give you a basic idea how to solve this.

Create a dictionary where the key is one element (like "A") and the value is an array of already added indices.

var dict = [String : [Int]]()

Then iterate thru all 5 arrays and append each element. Before appending it check in the dictionary if this element is already added somewhere else at the same index. If not you can add it, if yes, append another element and add the added index to the dict.

I hope you get the idea.

Darko
  • 9,655
  • 9
  • 36
  • 48
0

First some shortcuts :

  • Construct a type that easily displays a nested array in rows / columns.
  • Have a way to detect duplicate elements in columns (same element with same index in different row)

This can be found here

I did not include it in the answer because it is not part of the algorithm.


Extend Array to implement a function only when the Array is multidimensional and it's nested Element is Equatable.

extension Array where Element : _ArrayType, Element.Element : Equatable {

Self will be of type Array<Element> and will not recognise that it is cast-able to Array<Array<Element.Element>>. See the gist for the conversion.

    func entropy() -> [[Element.Element]]? { // returns nil when it is impossible

Create a matrix from the array.

        var matrix = self.matrix() // see gist for Matrix type

This nested function does the actual Array mutations. It swaps two elements in a row.

        func swap(row r:Int,column c:Int) {

            let nextColumn : Int = {
                if (c + 1) < matrix[r].count {
                    return c + 1
                } else {
                    return 0
                }
            }()

            let element = matrix[r][c]
            let neighbour = matrix[r][nextColumn]
            matrix[r][c] = neighbour
            matrix[r][nextColumn] = element

        }

This is the looping logic:

As long as the columns of the matrix contain duplicates -> go over each column -> find a duplicate -> swap it with a neighbour.

It is not very efficient. But you can try different functions to mutate the rows and see what works best.

        while matrix.columnsContainDuplicates() {
            for c in 0..<matrix.columns.count {
                let column = matrix.columns[c]
                if let dupeIndex = column.indexOfDuplicate() {
                    swap(row: dupeIndex, column: c)
                }
            }
        }

        return matrix.rows
    }
}

Complete code with and extra nested function to check if it is solvable. There might be other reasons why it might not be solvable.

extension Array where Element : _ArrayType, Element.Element : Equatable {

    func entropy() -> [[Element.Element]]? {

        var matrix = self.matrix() // this comes from an extension to Array: if nested, can be converted to a matrix

        // is there a possible configuration where no column has duplicates?
        func solvable() -> Bool {
            // this just checks if element x does not appear more than there are possible indexes.
            var uniqueElements : [Element.Element] = []
            var occurences : [Int] = []

            for row in matrix.rows {
                var rowCopy = row
                while let pop = rowCopy.popLast() {
                    if let index = uniqueElements.indexOf(pop) {
                        occurences[index] += 1
                        occurences[index]
                    } else {
                        uniqueElements.append(pop)
                        occurences.append(1)
                    }
                }
            }

            var highest = 0
            for times in occurences {
                if highest < times { highest = times }
            }

            if highest > matrix.columns.count {
                highest
                return false
            }

            return true
        }

        func swap(row r:Int,column c:Int) {

            let nextColumn : Int = {
                if (c + 1) < matrix[r].count {
                    return c + 1
                } else {
                    return 0
                }
            }()

            let element = matrix[r][c]
            let neighbour = matrix[r][nextColumn]
            matrix[r][c] = neighbour
            matrix[r][nextColumn] = element

        }

        guard solvable() else {
            return nil
        }

        while matrix.columnsContainDuplicates() {
            for c in 0..<matrix.columns.count {
                let column = matrix.columns[c]
                if let dupeIndex = column.indexOfDuplicate() {
                    swap(row: dupeIndex, column: c)
                }
            }
        }

        return matrix.rows
    }
}

Tests :

let test = [[4,3,2,1],[5,2,3,4],[1,6,3,4],[1,2,3,4]] // 8 loops
test.entropy() // [[3, 4, 1, 2], [4, 3, 2, 5], [6, 1, 4, 3], [1, 2, 3, 4]]

let test2 = [[1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4]] // 8 loops
test2.entropy() // [[3, 4, 1, 2], [4, 3, 2, 1], [2, 1, 4, 3], [1, 2, 3, 4]]
R Menke
  • 8,183
  • 4
  • 35
  • 63