Related
I used minimum edit distance algorithm to find the bundle of the most similar strings in an array.
So, I have to travel double for loop to compare all element.
If the data is large enough, this algorithm is Inefficient.
Is there a way to optimize?
let data = [
"10000", // count
"asdfqwerty", "asdfzxcvgh", "asdfpoiuyt",
...
]
for i in 1..<data.count {
let string = data[i]
for j in (i + 1)..<data.count {
let newMin = string.minimumEditDistance(other: data[j])
if min >= newMin {
// some logic
}
}
}
extension String {
public func minimumEditDistance(other: String, `default`: Int = 10) -> Int {
let m = self.count
let n = other.count
if m == 0 || n == 0 {
return `default`
}
var matrix = [[Int]](repeating: [Int](repeating: 0, count: n + 1), count: m + 1)
// initialize matrix
for index in 1...m {
// the distance of any first string to an empty second string
matrix[index][0] = index
}
for index in 1...n {
// the distance of any second string to an empty first string
matrix[0][index] = index
}
// compute Levenshtein distance
for (i, selfChar) in self.enumerated() {
for (j, otherChar) in other.enumerated() {
if otherChar == selfChar {
// substitution of equal symbols with cost 0
matrix[i + 1][j + 1] = matrix[i][j]
} else {
// minimum of the cost of insertion, deletion, or substitution
// added to the already computed costs in the corresponding cells
matrix[i + 1][j + 1] = Swift.min(matrix[i][j] + 1, matrix[i + 1][j] + 1, matrix[i][j + 1] + 1)
}
}
}
return matrix[m][n]
}
}
You can achieve desired behaviour by sorting your array using your minimumEditDistance as a sorting function and then taking first or last element (depends on how you define sorting) and what you need - min or max. It will likely run in O(N*log(N)) time. Which is already better than exponential.
As #Sultan mentioned, it will work not for all distances, as transitivity is applicable only to Metrics (functions that define a distance between each element of the set). You're using Levenstain distance as an editing distance algorithm, which is indeed a metric. The solution I mentioned should help to optimise in some circumstances.
I wonder why my solution to this LeetCode "Move Zeros" problem is slower than the majority of other submissions. Is there a better way to approach this problem to make it faster?
The question is as follows:
Given an array nums, write a function to move all 0's to the end of it while maintaining the relative order of the non-zero elements. You must do this in-place without making a copy of the array.
Example:
Input: [0,1,0,3,12]
Output: [1,3,12,0,0]
This is my solution:
func moveZeroes(_ nums: inout [Int]) {
var index = 0
for (i,n) in nums.enumerated()
{
if n != 0
{
nums[index] = n
index += 1
}
}
while index < nums.count
{
nums[index] = 0
index += 1
}
}
LeetCode gives me these statistics:
Runtime: 52 ms, faster than 40.50% of Swift online submissions for Move Zeroes.
Memory Usage: 19.4 MB, less than 13.33% of Swift online submissions for Move Zeroes.
EDIT 1:
If I approach the problem as follows, it does not move the zeros at the end,
EDIT 2:
Here is 36ms in-place solution for you :
class Solution {
func moveZeroes(_ nums: inout [Int]) {
if nums.count < 2 {
return
}
var j = 0
while j < nums.count, nums[j] != 0 {
j += 1
}
if j < nums.count - 1 {
for i in j+1..<nums.count {
if nums[i] != 0 {
nums.swapAt(i, j)
j += 1
}
}
}
}
}
From what I can see, it's likely other submissions are doing this
Check and count 0's in string
Remove 0's
Replace number of 0's at the end of the string
A logical method no doubt, but I'd say yours just picks the basic needs of the challenge and goes for it.
I would personally use:
input = input.filter { $0 != 0 } + input.filter { $0 == 0 }
which can be simplified to one pass:
let nonZeros = input.filter { $0 != 0 }
input = nonZeros + Array(repeating: 0, count: input.count - nonZeros.count)
EDIT: The simplest version without creating a new array would be some primitive version of bubble sort, e.g.:
var numZeros = 0
// iterate array from start to end
for (offset, element) in input.enumerated() {
if element == 0 {
// count every zero
numZeros += 1
} else if numZeros > 0 {
// move every non-zero element left
input[offset - numZeros] = element
// replace with zero
input[offset] = 0
}
}
Another approach is the half-stable-partition algorithm. The benefit is the items are swapped rather than removed and inserted/appended.
Half-stable means the order of the left side of the split point is preserved.
extension Array {
mutating func halfStablePartition(indexes : IndexSet) { // code is O(n)
guard var i = indexes.first, i < count else { return }
var j = index(after: i)
var k = indexes.integerGreaterThan(i) ?? endIndex
while j != endIndex {
if k != j { swapAt(i, j); formIndex(after: &i) }
else { k = indexes.integerGreaterThan(k) ?? endIndex }
formIndex(after: &j)
}
}
}
var input = [0,1,0,3,12]
let indices = IndexSet(input.indices.filter{input[$0] == 0})
input.halfStablePartition(indexes: indices)
Swift 4.2 or later using removeAll mutating method:
Mutating the input:
class Solution {
func moveZeroes(_ nums: inout [Int]) {
var counter = 0
nums.removeAll {
if $0 == 0 {
counter += 1
return true
}
return false
}
nums += repeatElement(0, count: counter)
}
}
A similar approach for Swift 4.1 or earlier
func moveZeroes(_ nums: inout [Int]) {
var counter = 0
nums.indices.reversed().forEach {
if nums[$0] == 0 {
counter += 1
nums.remove(at: $0)
}
}
nums += repeatElement(0, count: counter)
}
var input = [0,1,0,3,12]
moveZeroes(&input)
input // [1, 3, 12, 0, 0]
Non mutating approach:
func moveZeroes(_ nums: [Int]) -> [Int] {
var counter = 0
return nums.filter {
if $0 == 0 { counter += 1 }
return $0 != 0
} + repeatElement(0, count: counter)
}
let input = [0,1,0,3,12]
let zerosMoved = moveZeroes(input)
zerosMoved // [1, 3, 12, 0, 0]
For modifying array in place, and keeping it:
O(n) for Time Complexity
O(1) for Space Complexity
Cracked my head way to long for this one. The cleanest way if you swap element that is NOT zero:
func moveZeroes(_ nums: inout [Int]) {
// amount of swaps, will be used a as reference for next swap index
var j = 0
for (i, e) in nums.enumerated() {
if e != 0 {
nums.swapAt(j, i)
j += 1
}
}
}
One fast solution is to shift non-zero elements to the left by the amount of zeros encountered until then:
func moveZeroes(_ nums: inout [Int]) {
var offset = 0
for i in 0..<nums.count {
if nums[i] == 0 { offset += 1 }
else { nums.swapAt(i, i-offset) }
}
}
This solution takes exactly N steps, and at each step we either perform an addition, or a swap, which are both quite fast.
Your solution, on the other hand required two iterations, resulting in 2*N steps, which is why it was slower than other solutions.
I have an array that looks like this:
let arr = [1,2,3,4,5,6,7,8,9]
I know you can get min and max by:
let min = arr.min()
let max = arr.max()
But how do you get the median?
To get the median you can use the following:
let median = arr.sorted(by: <)[arr.count / 2]
In your case it will return 5.
As #Nirav pointed out [1,2,3,4,5,6,7,8] will return 5 but should return 4.5.
Use this instead:
func calculateMedian(array: [Int]) -> Float {
let sorted = array.sorted()
if sorted.count % 2 == 0 {
return Float((sorted[(sorted.count / 2)] + sorted[(sorted.count / 2) - 1])) / 2
} else {
return Float(sorted[(sorted.count - 1) / 2])
}
}
Usage:
let array = [1,2,3,4,5,6,7,8]
let m2 = calculateMedian(array: array) // 4.5
The median is defined as the number in the middle of the sequence. If there is not one middle number, then it's the average of the two middle numbers.
extension Array where Element == Int {
func median() -> Double {
let sortedArray = sorted()
if count % 2 != 0 {
return Double(sortedArray[count / 2])
} else {
return Double(sortedArray[count / 2] + sortedArray[count / 2 - 1]) / 2.0
}
}
}
Note that if the array is empty, the median is undefined. So a safe median function returns an optional, just like the min() and max() built-in methods do.
extension Array where Element == Int {
func median() -> Double? {
guard count > 0 else { return nil }
let sortedArray = self.sorted()
if count % 2 != 0 {
return Double(sortedArray[count/2])
} else {
return Double(sortedArray[count/2] + sortedArray[count/2 - 1]) / 2.0
}
}
}
With that defined, you can write:
if let median = arr.median() {
// do something
}
If someone (like me) likes two*-liners:
let sorted = arr.sorted(by: <)
let median = Double(sorted[arr.count/2] + sorted.reversed()[arr.count/2])/2.0
Algorithms that use sorted take O(n log n) time. That's typically not a problem for 9 numbers, but if your array is large, use an algorithm that completes in O(n) time. An example is this k-th largest element algorithm. It recursively partitions the array, but doesn’t have to go through all the work to sort it, so it’s much faster.
How can an algorithm in Swift 3.0 to find k largest element be optimized?
func largestElement(arr: [Int], k: Int) -> Int? {
let length = arr.count
if k > 0 && k <= length {
for k in 0..<length {
print(k)
}
let sorted = arr.sorted()
return sorted[length - k]
} else {
return nil
}
}
var arrayOfIntegers = [1, 6, 3, 9, 13, 15]
print(largestElement(arr: arrayOfIntegers, k: 5))
What is the best way to eliminate the sorted() function?
Quickselect is a well-known
algorithm to find the k-smallest element in an array with an
average complexity of O(N).
Here is a possible implementation of that algorithm in Swift 3.
It uses the same approach as the code example in the Wikipedia article,
but with iteration instead of recursion. Also the pivot element is
not moved to the front of the second partition. That saves some
swap operations but requires an additional check when updating
the lower bound.
extension Array where Element: Comparable {
func kSmallest(_ k: Int) -> Element {
precondition(1 <= k && k <= count, "k must be in the range 1...count")
var a = self // A mutable copy.
var low = startIndex
var high = endIndex
while high - low > 1 {
// Choose random pivot element:
let pivotElement = a[low + Int(arc4random_uniform(UInt32(high - low)))]
// Partition elements such that:
// a[i] < pivotElement for low <= i < pivotIndex,
// a[i] >= pivotElement for pivotIndex <= i < high.
var pivotIndex = low
while a[pivotIndex] < pivotElement {
pivotIndex += 1
}
for i in pivotIndex+1 ..< high {
if a[i] < pivotElement {
swap(&a[pivotIndex], &a[i])
pivotIndex += 1
}
}
if k <= pivotIndex {
// k-smallest element is in the first partition:
high = pivotIndex
} else if k == pivotIndex + 1 {
// Pivot element is the k-smallest:
return pivotElement
} else {
// k-smallest element is in the second partition
// (but not the pivot element)
low = pivotIndex
if a[low] == pivotElement {
low += 1
}
}
}
// Only single candidate left:
return a[low]
}
func kLargest(_ k: Int) -> Element {
return kSmallest(count + 1 - k)
}
}
Example:
let a = [2, 2, 3, 3, 1, 1, 4, 4]
for i in 1...a.count {
let l = a.kLargest(i)
print(l)
}
// 4 4 3 3 2 2 1 1
This approach of sorting first, then just accessing, is a straight forward technique, but isn't very fast. It's O(N * log_2(N)). Small performance optimisations you make to this code can make small improvements but will never improve that asymptotic efficiency. If performance is a concern, you better use the Quick Select algorithm, or some other linear time (O(N)) algorithm.
As a side note, here are some improvements I made to your alogirthm to make it more concise, modern swift.
func largestElement(arr: [Int], k: Int) -> Int? {
guard arr.indices ~= k else { return nil }
return arr.sorted()[arr.count - k]
}
Suppose I have an array of M elements, all numbers, negative or positive or zero.
Can anyone suggest an algorithm to select N elements from the array, such that the sum of these N elements is the smallest possible positive number?
Take this array for example:
-1000,-700,-400,-200,-100,-50,10,100,300,600,800,1200
Now I have to select any 5 elements such that their sum is the smallest possible positive number.
Formulation
For i = 1, ..., M:
Let a_i be the ith number in your list of candidates
Let x_i denote whether the ith number is included in your set of N chosen numbers
Then you want to solve the following integer programming problem.
minimize: sum(a_i * x_i)
with respect to: x_i
subject to:
(1) sum(a_i * x_i) >= 0
(2) sum(x_i) = N
(3) x_i in {0, 1}
You can apply an integer program solver "out of the box" to this problem to find the optimal solution or a suboptimal solution with controllable precision.
Resources
Integer programming
Explanation of branch-and-bound integer program solver
If you want to find the best possible solution, you can simply use brute force ie. try all posible combinations of fiwe numbers.
Something like this very quick and dirty algorithm:
public List<Integer> findLeastPositivSum(List<Integer> numbers) {
List<Integer> result;
Integer resultSum;
List<Integer> subresult, subresult2, subresult3, subresult4, subresult5;
for (int i = 0; i < numbers.size() - 4; i++) {
subresult = new ArrayList<Integer>();
subresult.add(numbers.get(i));
for (int j = i + 1; j < numbers.size() - 3; j++) {
subresult2 = new ArrayList<Integer>(subresult);
subresult2.add(j);
for (int k = j + 1; k < numbers.size() - 2; k++) {
subresult3 = new ArrayList<Integer>(subresult2);
subresult3.add(k);
for (int l = k + 1; l < numbers.size() - 1; l++) {
subresult4 = new ArrayList<Integer>(subresult3);
subresult4.add(k);
for (int m = l + 1; m < numbers.size(); m++) {
subresult5 = new ArrayList<Integer>(subresult4);
subresult5.add(k);
Integer subresultSum = sum(subresult5);
if (subresultSum > 0) {
if (result == null || resultSum > subresultSum) {
result = subresult;
}
}
}
}
}
}
}
return result;
}
public Integer sum(List<Integer> list) {
Integer result = 0;
for (Integer integer : list) {
result += integer;
}
return result;
}
This is really quick and dirty algorithm, it can be done more elegantly. I can provide cleaner algorithm e.g. using recursion.
It can be also further optimized. E.g. you can remove similar numbers from input list as first step.
Let initial array be shorted already, or i guess this will work even when it isnt shorted..
N -> Length of array
M -> Element req.
R[] -> Answer
TEMP[] -> For calculations
minSum -> minSum
A[] -> Initial input
All above variables are globally defined
int find(int A[],int start,int left)
{
if(left=0)
{
//sum elements in TEMP[] and save it as curSum
if(curSum<minSum)
{
minSum=curSum;
//assign elements from TEMP[] to R[] (i.e. our answer)
}
}
for(i=start;i<=(N-left);i++)
{
if(left==M)
curSum=0;
TEMP[left-1]=A[i];
find(A[],i+1,left-1);
}
}
// Made it in hurry so maybe some error would be existing..
Working solution on ideone :
http://ideone.com/YN8PeW
I suppose Kadane’s Algorithm would do the trick, although it is for the maximum sum but I have also implemented it to find the minimum sum, though can't find the code right now.
Here's something sub optimal in Haskell, which (as with many of my ideas) could probably be further and better optimized. It goes something like this:
Sort the array (I got interesting results by trying both ascending and descending)
B N = first N elements of the array
B (i), for i > N = best candidate; where (assuming integers) if they are both less than 1, the candidates are compared by the absolute value of their sums; if they are both 1 or greater, by their sums; and if only one candidate is greater than 0 then that candidate is chosen. If a candidate's sum is 1, return that candidate as the answer. The candidates are:
B (i-1), B (i-1)[2,3,4..N] ++ array [i], B (i-1)[1,3,4..N] ++ array [i]...B (i-1)[1,2..N-1] ++ array [i]
B (i-2)[2,3,4..N] ++ array [i], B (i-2)[1,3,4..N] ++ array [i]...B (i-2)[1,2..N-1] ++ array [i]
...
B (N)[2,3,4..N] ++ array [i], B (N)[1,3,4..N] ++ array [i]...B (N)[1,2..N-1] ++ array [i]
Note that for the part of the array where the numbers are negative (in the case of ascending sort) or positive (in the case of descending sort), step 3 can be done immediately without calculations.
Output:
*Main> least 5 "desc" [-1000,-700,-400,-200,-100,-50,10,100,300,600,800,1200]
(10,[-1000,600,300,100,10])
(0.02 secs, 1106836 bytes)
*Main> least 5 "asc" [-1000,-700,-400,-200,-100,-50,10,100,300,600,800,1200]
(50,[300,100,-200,-100,-50])
(0.02 secs, 1097492 bytes)
*Main> main -- 10000 random numbers ranging from -100000 to 100000
(1,[-106,4,-40,74,69])
(1.77 secs, 108964888 bytes)
Code:
import Data.Map (fromList, insert, (!))
import Data.List (minimumBy,tails,sort)
import Control.Monad.Random hiding (fromList)
array = [-1000,-700,-400,-200,-100,-50,10,100,300,600,800,1200]
least n rev arr = comb (fromList listStart) [fst (last listStart) + 1..m]
where
m = length arr
r = if rev == "asc" then False else True
sorted = (if r then reverse else id) (sort arr)
listStart = if null lStart
then [(n,(sum $ take n sorted,take n sorted))]
else lStart
lStart = zip [n..]
. takeWhile (all (if r then (>0) else (<0)) . snd)
. foldr (\a b -> let c = take n (drop a sorted) in (sum c,c) : b) []
$ [0..]
s = fromList (zip [1..] sorted)
comb list [] = list ! m
comb list (i:is)
| fst (list ! (i-1)) == 1 = list ! (i-1)
| otherwise = comb updatedMap is
where updatedMap = insert i bestCandidate list
bestCandidate = comb' (list!(i - 1)) [i - 1,i - 2..n] where
comb' best [] = best
comb' best (j:js)
| fst best == 1 = best
| otherwise =
let s' = map (\x -> (sum x,x))
. (take n . map (take (n - 1)) . tails . cycle)
$ snd (list!j)
t = s!i
candidate = minimumBy compare' (map (add t) s')
in comb' (minimumBy compare' [candidate,best]) js
add x y#(a,b) = (x + a,x:b)
compare' a#(a',_) b#(b',_)
| a' < 1 = if b' < 1 then compare (abs a') (abs b') else GT
| otherwise = if b' < 1 then LT else compare a' b'
rnd :: (RandomGen g) => Rand g Int
rnd = getRandomR (-100000,100000)
main = do
values <- evalRandIO (sequence (replicate (10000) rnd))
putStrLn (show $ least 5 "desc" values)
Assumption: M is the original array
Pesudocode
S = sort(M);
R = [];
sum = 0;
for(i=0, i < length(S); i++){
sum = sum + S[i];
if(sum < 1){
R.push(S[i]);
}else{
return R;
}
}