Matriz bidimensional en Swift

109

Me confunden tanto las matrices 2D en Swift. Déjame describirte paso a paso. ¿Y podría corregirme si me equivoco?

Ante todo; declaración de una matriz vacía:

class test{
    var my2Darr = Int[][]()
}

En segundo lugar llene la matriz. (como my2Darr[i][j] = 0donde i, j son variables de ciclo for)

class test {
    var my2Darr = Int[][]()
    init() {
        for(var i:Int=0;i<10;i++) {
            for(var j:Int=0;j<10;j++) {
                my2Darr[i][j]=18   /*  Is this correct?  */
            }
        }
    }
}

Y por último, elemento de edición de una matriz

class test {
    var my2Darr = Int[][]()
    init() {
        ....  //same as up code
    }
    func edit(number:Int,index:Int){
        my2Darr[index][index] = number
        // Is this correct? and What if index is bigger
        // than i or j... Can we control that like 
        if (my2Darr[i][j] == nil) { ...  }   */
    }
}
Antiokhos
fuente
¿Tiene problemas con su enfoque?
Alex Wayne
2
Para que lo sepas, todo tu segundo paso se puede reducir a esto. var my2DArray = Array(count: 10, repeatedValue: Array(count: 10, repeatedValue: 18))Y realmente deberías actualizar a una versión beta más nueva. Int[][]()ya no es una sintaxis válida. Se ha cambiado a [[Int]]().
Mick MacCallum
1
La inicialización 2D que usa valores repetidos no funcionará. Todas las filas apuntarán a la misma submatriz y, por lo tanto, no podrán escribirse de forma única.
hotpaw2

Respuestas:

228

Definir matriz mutable

// 2 dimensional array of arrays of Ints 
var arr = [[Int]]() 

O:

// 2 dimensional array of arrays of Ints 
var arr: [[Int]] = [] 

O si necesita una matriz de tamaño predefinido (como lo menciona @ 0x7fffffff en los comentarios):

// 2 dimensional array of arrays of Ints set to 0. Arrays size is 10x5
var arr = Array(count: 3, repeatedValue: Array(count: 2, repeatedValue: 0))

// ...and for Swift 3+:
var arr = Array(repeating: Array(repeating: 0, count: 2), count: 3)

Cambiar elemento en la posición

arr[0][1] = 18

O

let myVar = 18
arr[0][1] = myVar

Cambiar submatriz

arr[1] = [123, 456, 789] 

O

arr[0] += 234

O

arr[0] += [345, 678]

Si tenía una matriz 3x2 de 0 (ceros) antes de estos cambios, ahora tiene:

[
  [0, 0, 234, 345, 678], // 5 elements!
  [123, 456, 789],
  [0, 0]
]

Así que tenga en cuenta que las submatrices son mutables y puede redefinir la matriz inicial que representa la matriz.

Examinar tamaño / límites antes del acceso

let a = 0
let b = 1

if arr.count > a && arr[a].count > b {
    println(arr[a][b])
}

Observaciones: las mismas reglas de marcado para matrices de 3 y N dimensiones.

Keenle
fuente
ok, una pregunta tonta: cómo asignamos esa matriz, en C lo hacemos así: arr [i] [j] = myVar; pero rápidamente, cuando trato de hacer lo mismo, recibí este error "'[([(Int)])]. El tipo' no tiene un miembro llamado 'subíndice'"
Antiokhos
Si ha arrdefinido como en la respuesta, entonces myVardebería ser Int, ¿verdad?
Keenle
sí, es int. Y muchas gracias por la respuesta detallada ... ahora está claro: D
Antiokhos
6
En Swift 3, para copiadores:var arr = Int(repeating: Int(repeating: 0, count: 2), count: 3)
kar
1
En Swift 4.2: por ejemplo, 3 filas, 2 columnas, 3 * 2var arr = Array(count: 2, repeatedValue: Array(count: 3, repeatedValue: 0))
Zgpeace
27

De los documentos:

Puede crear matrices multidimensionales anidando pares de corchetes, donde el nombre del tipo base de los elementos está contenido en el par más interno de corchetes. Por ejemplo, puede crear una matriz tridimensional de números enteros utilizando tres conjuntos de corchetes:

var array3D: [[[Int]]] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]

Al acceder a los elementos en una matriz multidimensional, el índice de subíndice más a la izquierda se refiere al elemento en ese índice en la matriz más externa. El siguiente índice de subíndice a la derecha se refiere al elemento en ese índice en la matriz que está anidado en un nivel. Y así sucesivamente. Esto significa que en el ejemplo anterior, array3D [0] se refiere a [[1, 2], [3, 4]], array3D [0] [1] se refiere a [3, 4] y array3D [0] [1 ] [1] se refiere al valor 4.

Woodstock
fuente
17

Hazlo Genérico Swift 4

struct Matrix<T> {
    let rows: Int, columns: Int
    var grid: [T]
    init(rows: Int, columns: Int,defaultValue: T) {
        self.rows = rows
        self.columns = columns
        grid = Array(repeating: defaultValue, count: rows * columns) as! [T]
    }
    func indexIsValid(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }
    subscript(row: Int, column: Int) -> T {
        get {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}


var matrix:Matrix<Bool> = Matrix(rows: 1000, columns: 1000,defaultValue:false)

matrix[0,10] = true


print(matrix[0,10])
dimo hamdy
fuente
Adapté tu respuesta para crear una matriz toroidal 2D. ¡Muchas gracias! gist.github.com/amiantos/bb0f313da1ee686f4f69b8b44f3cd184
Brad Root
16

Debe tener cuidado al usar Array(repeating: Array(repeating: {value}, count: 80), count: 24).

Si el valor es un objeto, que está inicializado por MyClass(), utilizarán la misma referencia.

Array(repeating: Array(repeating: MyClass(), count: 80), count: 24)no crea una nueva instancia de MyClassen cada elemento de la matriz. Este método solo crea MyClassuna vez y lo coloca en la matriz.

Esta es una forma segura de inicializar una matriz multidimensional.

private var matrix: [[MyClass]] = MyClass.newMatrix()

private static func newMatrix() -> [[MyClass]] {
    var matrix: [[MyClass]] = []

    for i in 0...23 {
        matrix.append( [] )

        for _ in 0...79 {
            matrix[i].append( MyClass() )
        }
    }

    return matrix
}
Kimi Chiu
fuente
Hola, ¿podemos mejorar eso como una extensión con el tipo "anyObject"?
Antiokhos
Buen comentario sobre el problema con los tipos de referencia. Sin embargo, ¿por qué escribes Array(repeating: {value}, could 80)con llaves {value}? Eso crearía una serie de cierres, ¿no es así?
Duncan C
¿O es {value}una metanotación para "algún valor de tipo AnyObject" (un tipo de referencia)?
Duncan C
Pasé casi una hora buscando un error debido a este problema ...
Matheus Weber
13

En Swift 4

var arr = Array(repeating: Array(repeating: 0, count: 2), count: 3)
// [[0, 0], [0, 0], [0, 0]]
Ankit garg
fuente
10

De acuerdo con los documentos de Apple para swift 4.1, puede usar esta estructura tan fácilmente para crear una matriz 2D:

Enlace: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Subscripts.html

Muestra de código:

struct Matrix {
    let rows: Int, columns: Int
    var grid: [Double]
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        grid = Array(repeating: 0.0, count: rows * columns)
    }
    func indexIsValid(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }
    subscript(row: Int, column: Int) -> Double {
        get {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}
Keyhan Kamangar
fuente
1
Me gusta. Es una reminiscencia de la aritmética del puntero C. Sin embargo, sería mejor si se reescribiera usando Generics, por lo que se aplicaría a matrices bidimensionales de cualquier tipo de datos. De hecho, puede utilizar este enfoque para crear matrices de cualquier dimensión arbitraria.
Duncan C
1
@vacawama, genial, excepto que su matriz n-dimensional tiene el mismo problema que todas las soluciones que pueblan la matriz usando Array(repeating:count:). Vea el comentario que publiqué en su otra respuesta.
Duncan C
6

Antes de utilizar matrices multidimensionales en Swift, considere su impacto en el rendimiento . En mis pruebas, la matriz aplanada funcionó casi 2 veces mejor que la versión 2D:

var table = [Int](repeating: 0, count: size * size)
let array = [Int](1...size)
for row in 0..<size {
    for column in 0..<size {
        let val = array[row] * array[column]
        // assign
        table[row * size + column] = val
    }
}

Tiempo medio de ejecución para llenar una matriz de 50x50: 82,9 ms

vs.

var table = [[Int]](repeating: [Int](repeating: 0, count: size), count: size)
let array = [Int](1...size)
for row in 0..<size {
    for column in 0..<size {
        // assign
        table[row][column] = val
    }
}

Tiempo medio de ejecución para llenar una matriz 2D de 50x50: 135 ms

Ambos algoritmos son O (n ^ 2), por lo que la diferencia en los tiempos de ejecución se debe a la forma en que inicializamos la tabla.

Finalmente, lo peor que puede hacer es usar append()para agregar nuevos elementos. Eso resultó ser el más lento en mis pruebas:

var table = [Int]()    
let array = [Int](1...size)
for row in 0..<size {
    for column in 0..<size {
        table.append(val)
    }
}

Tiempo de ejecución promedio para llenar una matriz de 50x50 usando append (): 2.59s

Conclusión

Evite las matrices multidimensionales y utilice el acceso por índice si la velocidad de ejecución es importante. Las matrices 1D son más eficaces, pero su código puede ser un poco más difícil de entender.

Puede ejecutar las pruebas de rendimiento usted mismo después de descargar el proyecto de demostración desde mi repositorio de GitHub: https://github.com/nyisztor/swift-algorithms/tree/master/big-o-src/Big-O.playground

Karoly Nyisztor
fuente
0

Esto se puede hacer en una sola línea.

Rápido 5

var my2DArray = (0..<4).map { _ in Array(0..<) }

También puede asignarlo a instancias de cualquier clase o estructura de su elección

struct MyStructCouldBeAClass {
    var x: Int
    var y: Int
}

var my2DArray: [[MyStructCouldBeAClass]] = (0..<2).map { x in
    Array(0..<2).map { MyStructCouldBeAClass(x: x, y: $0)}
}
pimisi
fuente