How to multiply two arrays element-wise

2020-01-27 08:03发布

I need to multiply an array by another array element-wise, just like the Hadamard product of vectors in math. For example:

A = [1,2,3,4]
B = [2,3,4,5]
C = A*B = [2,6,12,20]

I can't even figure out the code, I've tried doing so element by element but this seems too messy of a solution for me, any ideas?

5条回答
叛逆
2楼-- · 2020-01-27 08:09

Accelerate framework

For the topic of vector multiplication, another alternative (in addition to the neat simd covered by @appzYourLife's answer) is making use of the Accelerate framework. In this case, specifically the vDSP methods vDSP_vmul and vDSP_vmuld,

func vDSP_vmul(UnsafePointer<Float>, vDSP_Stride, 
               UnsafePointer<Float>, vDSP_Stride, 
               UnsafeMutablePointer<Float>, vDSP_Stride, vDSP_Length)

func vDSP_vmulD(UnsafePointer<Double>, vDSP_Stride, 
                UnsafePointer<Double>, vDSP_Stride, 
                UnsafeMutablePointer<Double>, vDSP_Stride, vDSP_Length)

E.g., the latter used for element-by-element multiplication of two vectors of Double values:

import Accelerate

let a = [1.5, 2.5, 16.5, 7.5, 3.0]
let b = [3.0, 4.5, 0.25, 3.5, 6.25]
var result = [Double](repeating: 0.0, count: a.count)

if a.count == b.count {
    vDSP_vmulD(a, 1, b, 1, &result, 1, vDSP_Length(a.count))
    print(result) // [4.5, 11.25, 4.125, 26.25, 18.75]
}

Note that using Accelerate is not as user friendly and safe as the alternative methods, as the vector arguments to vDSP_vmulD are captured as unsafe pointers (UnsafePointer<Double>), and that it's our responsibility to make sure that the input vectors are of same length, as well as the result vector being properly allocated prior to the vector multiplication by vDSP_vmulD.

查看更多
何必那么认真
3楼-- · 2020-01-27 08:13

"Zipping" the two arrays gives a sequence of tuples (a_i, b_i) which can then be multiplied element-wise:

let A = [1,2,3,4]
let B = [2,3,4,5]

let C = zip(A, B).map { $0 * $1 }

print(C) // [2, 6, 12, 20]

(If the arrays have different length then zip silently ignores the extra elements of the longer array.)

As @appzYourLife correctly said, you can also pass the multiplication operator directly as an argument to map instead of a closure expression:

let C = zip(A, B).map(*)
查看更多
Lonely孤独者°
4楼-- · 2020-01-27 08:16

With Swift 5, you can use one of the following ways in order to solve you problem.


#1. Using SIMD vector types

The following Playground sample code shows an element-wise multiplication using SIMD4:

let vector1 = SIMD4(1, 2, 3, 4)
let vector2 = SIMD4(2, 3, 4, 5)

let vector3 = vector1 &* vector2
print(vector3) // prints: SIMD4<Int>(2, 6, 12, 20)

Note that SIMD protocol conforms to ExpressibleByArrayLiteral. Therefore, you can initialize your vector using an array literal:

var vector1: SIMD4 = [1, 2, 3, 4]
let vector2: SIMD4 = [2, 3, 4, 5]

vector1 &*= vector2
print(vector1) // prints: SIMD4<Int>(2, 6, 12, 20)

#2. Using a custom type that conforms to Numeric and ExpressibleByArrayLiteral protocols

You can build your own custom type that conforms to Numeric and ExpressibleByArrayLiteral. The following Playground sample code shows how to implement and use it:

struct Vector {
    let x, y: Int

    init(_ x: Int, _ y: Int) {
        self.x = x
        self.y = y
    }
}
extension Vector: AdditiveArithmetic {
    static var zero: Vector {
        return Vector(0, 0)
    }

    static func +(lhs: Vector, rhs: Vector) -> Vector {
        return Vector(lhs.x + rhs.x, lhs.y + rhs.y)
    }

    static func +=(lhs: inout Vector, rhs: Vector) {
        lhs = lhs + rhs
    }

    static func -(lhs: Vector, rhs: Vector) -> Vector {
        return Vector(lhs.x - rhs.x, lhs.y - rhs.y)
    }

    static func -=(lhs: inout Vector, rhs: Vector) {
        lhs = lhs - rhs
    }
}
extension Vector: ExpressibleByIntegerLiteral {
    init(integerLiteral value: Int) {
        x = value
        y = value
    }
}
import Darwin

extension Vector: Numeric {
    var magnitude: Int {
        // Implement according to your needs
        return Int(Darwin.sqrt(Double(x * x + y * y)))
    }

    init?<T>(exactly source: T) where T : BinaryInteger {
        guard let source = source as? Int else  {
            return nil
        }
        x = source
        y = source
    }

    static func *(lhs: Vector, rhs: Vector) -> Vector {
        return Vector(lhs.x * rhs.y, lhs.y * rhs.x)
    }

    static func *=(lhs: inout Vector, rhs: Vector) {
        lhs = lhs * rhs
    }
}
extension Vector: ExpressibleByArrayLiteral {
    init(arrayLiteral elements: Int...) {
        assert(elements.count == 2, "arrayLiteral should have exactly 2 elements")
        self.x = elements[0]
        self.y = elements[1]
    }
}

Usage:

let vector1 = Vector(1, 2)
let vector2 = Vector(2, 3)

let vector3 = vector1 * vector2
print(vector3) // prints: Vector(x: 3, y: 4)
let vector1: Vector = [1, 2]
let vector2: Vector = [2, 3]

let vector3 = vector1 * vector2
print(vector3) // prints: Vector(x: 3, y: 4)
查看更多
贪生不怕死
5楼-- · 2020-01-27 08:19

Single Instruction Multiple Data

If your vectors have exactly 4 components you can use the superfast simd (Single Instruction Multiple Data) instructions provided by iOS.

It uses the CPU to perform parallel computations.

Given 2 vectors of 4 components of Int32

import simd

let a = int4(1, 2, 3, 4)
let b = int4(2, 3, 4, 5)

you can multiply each component

let res = a &* b // int4(2, 6, 12, 20)

As noted by Martin R , array of float(s) or of double(s) are provided as well by the simd module.

查看更多
Anthone
6楼-- · 2020-01-27 08:27
 let A = [1,2,3,4]
 let B = [2,3,4,5]
 var C = [Int]()

 A.enumerated().forEach{ index, value in
     return C.append(value * B[index])
 }
查看更多
登录 后发表回答