How do I call my binary search function in swift? - c

I'm an old C programmer. I'm trying to implement some code in Swift and needed a binary search algorithm that would return the closest element if an exact match isn't available.
I searched for a bit and either the code was invoking so many Swift concepts at once as to be opaque to my C eyes or it wasn't suitable for my use case.
I decided to port some old C code to get the job done and wrote the following:
public func bsearch(arr:[Any], target:Double, bcompare:(Any,Double)->Int)-> Int{
// v is array being searched
// target is what we're looking for
// bcompare is a pointer to user-supplied comparison function
// bcompare returns -1 if left element < right element, 0 if =, 1 if >
// target must be same type as v element being compared
// returns index of array element that is closest to <= target
// note! indexed returned may not match target value
var lo, hi, mid:Int
hi = v.count-1
if hi <= 0 {return -1}
lo = 0
while ((hi-lo) > 1) {
mid = (hi+lo)/2
if( bcompare(v[mid], target) == -1){
lo = mid + 1
}
else{
hi = mid
}
}
if bcompare(v[hi],target) == 0{
return hi
}
return lo
}
func eleCompare(left:locArrayele,right:Double)->Int{
if right < left.degrees{
return -1
}
else if right == left.degrees{
return 0
}
else {
return 1
}
}
In C, you can pass search functions pointers to structures and tell the compiler how to interpret the memory blocks in the comparison functions that you also pass to the search function. The comparison function reference is just another memory pointer and no parameters are needed.
I assumed a Swift "Any" declaration was equivalent to a pointer reference and wrote the above code with that idea in mind. The compiler complained about declaring the search target as an Any when the comparison function referred to the target as a double. To satisfy the compiler, I declared the target as a double and the code compiled fine.
My problem now is to actually test the code. No matter how I try to invoke the search function, the compiler is unhappy. This test snippet is the closest I could get to satisfying the compiler.
class locArrayele{
public var degrees = CLLocationDegrees()
public var ix = Int()
init( degrees:CLLocationDegrees, i:Int){
self.degrees = degrees
self.ix = i
}
}
public var latArray : [locArrayele] = []
.
.
.
ix = bsearch(v: latArray, target:35.0, bcompare: eleCompare )
print(" when lat is 35, closest ix is \(ix))
Apparently, the compiler wants me to supply parameters to eleCompare, a task I expect bsearch to do as it executes.
How do I invoke the code? I realize I'm c-ifing Swift but I'm just trying to get something to work. Elegance can come later as I get comfortable in the language.

You need to make your bsearch() generic. You have two types that can vary: the first type is the one that the array v contains, and the other is the target's type.
Change your first line to:
public func bsearch<T, U>(v: [T], target: U, bcompare: (T, U) -> Int) -> Int {
You don't have to use 2 different types when you call it, but you can.
Example: String and Int
This example has an array of words of type [String] and it is searching for the word with 5 letters, so the target in an Int.
let words = ["a", "to", "the", "seven", "butter"]
func compareNameLength(left: String, right: Int) -> Int {
if left.count < right {
return -1
} else if left.count == right {
return 0
} else {
return 1
}
}
// search for the 5 letter word
let i = bsearch(v: words, target: 5, bcompare: compareNameLength)
print(words[i])
seven
Example: Int and Int
This example has an [Int] containing prime numbers, and it is searching for a prime closest to a number without going over, so the target is an Int.
let primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]
func compareInt(left: Int, right: Int) -> Int {
if left < right {
return -1
} else if left == right {
return 0
} else {
return 1
}
}
// search for closest prime to 8
let p = bsearch(v: primes, target: 8, bcompare: compareInt)
print(primes[p])
7

Related

Scala - How do I modify an input array passed to a method by reference?

Problem Statement
I will try to elaborate the case by means of a scenario. Lets take this question for instance.
Link to question: https://leetcode.com/problems/remove-element/
Given an array nums and a value target, remove all instances of
that value in-place and return the new length.
Do not allocate extra space for another array, you must do this by modifying the input array in-place with O(1) extra memory.
Example: Given nums = [0,1,2,2,3,0,4,2], target = 2; the output =
5 (number of elements not equal to target) and modify the array to
[0,1,3,0,4]
The order of elements can be changed. It doesn't matter what you leave
beyond the new length.
My Approach
Step-1: Identify all the elements which are equal to the given target and move them to right hand side of the array while maintaining a counter.
Step-2: Drop all the elements from right.
Step-3: return (n - counter), where n is the array length and counter is the number of elements equal to target.
Below is the implementation of the same:
object RemoveElement {
// Link to question: https://leetcode.com/problems/remove-element/
def main(args: Array[String]): Unit = {
var nums = Array(3,2,2,3)
val target = 3
val result = removeElement(nums, target)
// nums = nums.dropRight(_.equals(target)) // POINT 1
println(s"Result: ${result}, Modified Array: [${nums.mkString(", ")}]")
}
def removeElement(nums: Array[Int], target: Int): Int = {
val n = nums.length
var left, counter = 0
var right = n - 1
while(left < right){
if(nums(left) != target){
left += 1
}
else {
// Find position of the elements which is not equal to target
if(nums(right) == target){
counter += 1
right -= 1
}
else{
// Swap the elements
counter += 1
val temp = nums(left)
nums(left) = nums(right)
nums(right) = temp
left += 1
right -= 1
}
}
}
// nums.dropWhile(_.equals(target)) // POINT 2
// nums = nums.dropRight(_.equals(target)) // POINT 3
return (n - counter)
}
}
POINT - 1: Makes absolute sense as the array nums is in the scope of main method, therefore, the statement would work as charm.
POINT - 2: These lines has no impact to the array nums.
POINT - 3: Gives error. I understand that the input argument (array nums) is of type val (i.e. passed by reference, and hence immutable within the scope of the method removeElement).
If I had an option of creating a new array, there wouldn't be any issue. But if I am required to return the modified array by adding/removing the elements (like in this question) to the calling method, how do I achieve that in Scala?
To make the case more generic, what is the way by which we can modify the input collections (passed as arguments) in Scala methods?
P.S.: If I do not remove elements from the input array itself, LeetCode fails my submission with below message:
How do I modify an input array passed to a method by reference?
Scala does not support pass-by-reference. The default is pass-by-value (or more precisely, a special case of pass-by-value which is sometimes known as call-by-object, call-by-sharing, or call-by-object-sharing). Scala also supports call-by-name.
So, you simply cannot pass an array to a method by reference in Scala. You will have to use another language which supports pass-by-reference such as C# (with the ref keyword) or C++. (Note that Java also doesn't support pass-by-reference.)
Something like this
object Solution {
def removeElement(nums: Array[Int], `val`: Int): Int = {
var p: Int = 0
nums.foreach(v => {
if (v != `val`) {
nums(p) = v
p += 1
}
})
p
}
}

Merge duplicate longs in an array

I'm trying to merge/multiply duplicate longs in an array recursively.
So if I have something like that:
long[] arr = {3, 5, 6, 6, 7} => long[] arr = {3, 5, 36, 7}
That's what I've got:
public static long[] merge(long[] ns, int i, Merger m) {
m.merge();
if(i > ns.length) return new long[0];
if(i < 0) return merge(ns, 0, m);
else {
if(ns[i] != ns[i+1]) {
return append(merge(ns, i-1, m), ns[i+1]);
}
else {
return append(merge(ns, i-1, m), ns[i] * ns[i+1]);
}
}
public long[] append(long[] old, long newLast) {
long[] result = Arrays.copyOf(old, old.length + 1);
result[old.length] = newLast;
return result;
}
}
But it stucks in its recursion.
There are multiple cases that are not clear from the approach that you've taken.
What happens when there are multiple instances of the same value? Do they simply get multiplied? In your current logic, you check whether ns[i] != ns[i+1], which assumes that a. the list if sorted, .b. that occurrences come up only in pairs.
To see why (a) holds, your current approach would not multiply the two 6s if your input list were [3,6,5,6,7]. Is this a valid assumption to make?
To see why (b) holds, assume you had for input [1,3,5,6,6,6,7]. In this case, on multiplying the first two occurrences of 6, your resultant list would be [1,3,5,36,6,7], and your current logic would not end up multiplying 36 and 6.
Is this intended?
Before implementing a recursive solution, it would be instructional to write out the iterative implementation first. That way, the problem specification will become clearer to you.
Assuming these two assumptions hold for the specific problem you're trying to solve, the implementation below works.
(Note - this is implemented in Python. if you're looking for a Java specific solution, you should modify your question specifying it + add a Java tag to your post. Someone fluent in Java can then help you out. This solution tries to resemble your approach as closely as possible.)
def merge(ns, i, resultant_list = None):
if resultant_list is None:
resultant_list = []
if i > len(ns)-1:
return resultant_list
else:
if i == len(ns)-1:
append(resultant_list, ns[i])
return resultant_list
elif(ns[i] != ns[i+1]):
append(resultant_list, ns[i])
return merge(ns, i+1, resultant_list)
else:
append(resultant_list, ns[i] * ns[i+1])
return merge(ns, i+2, resultant_list)
def append(old, newLast):
old.append(newLast)
return old

Type Int does not conform to protocol sequence

I have the following code in Swift 3:
var numbers = [1,2,1]
for number in numbers.count - 1 { // error
if numbers[number] < numbers[number + 1] {
print(number)
}
}
I am checking if the value on the index [number] is always higher than the value on the index [number + 1]. I am getting an error:
Type Int does not conform to protocol sequence
Any idea?
It may be swift.
You can use this iteration.
for number in 0..<(numbers.count-1)
The error is because Int is not a Sequence. You can create a range as already suggested, which does conform to a sequence and will allow iteration using for in.
One way to make Int conform to a sequence is:
extension Int: Sequence {
public func makeIterator() -> CountableRange<Int>.Iterator {
return (0..<self).makeIterator()
}
}
Which would then allow using it as a sequence with for in.
for i in 5 {
print(i)
}
but I wouldn't recommend doing this. It's only to demonstrate the power of protocols but would probably be confusing in an actual codebase.
From you example, it looks like you are trying to compare consecutive elements of the collection. A custom iterator can do just that while keeping the code fairly readable:
public struct ConsecutiveSequence<T: IteratorProtocol>: IteratorProtocol, Sequence {
private var base: T
private var index: Int
private var previous: T.Element?
init(_ base: T) {
self.base = base
self.index = 0
}
public typealias Element = (T.Element, T.Element)
public mutating func next() -> Element? {
guard let first = previous ?? base.next(), let second = base.next() else {
return nil
}
previous = second
return (first, second)
}
}
extension Sequence {
public func makeConsecutiveIterator() -> ConsecutiveSequence<Self.Iterator> {
return ConsecutiveSequence(self.makeIterator())
}
}
which can be used as:
for (x, y) in [1,2,3,4].makeConsecutiveIterator() {
if (x < y) {
print(x)
}
}
In the above example, the iterator will go over the following pairs:
(1, 2)
(2, 3)
(3, 4)
This maybe a little late but you could have done:
for number in numbers { }
instead of:
for number in numbers.count - 1 { }
For a for loop to work a sequence (range) is needed. A sequence consists of a stating a value, an ending value and everything in between. This means that a for loop can be told to loop through a range with ether
for number in 0...numbers.count-1 { } `or` for number in numbers { }
Both example give the nesasery sequences. Where as:
for number in numbers.count - 1 { }
Only gives one value that could either be the starting or the ending value, making it impossible to work out how many time the for loop will have to run.
For more information see Apple's swift control flow documnetation
This error can also come about if you try to enumerate an array instead of the enumerated array. For example:
for (index, element) in [0, 3, 4] {
}
Should be:
for (index, element) in [0, 3, 4].enumerated() {
}
So first you need to understand what is sequence..
A type that provides sequential, iterated access to its elements.
A sequence is a list of values that you can step through one at a time. The most common way to iterate over the elements of a sequence is to use a for-in loop:
let oneTwoThree = 1...3. // Sequence
for loop actually means
For number in Sequences {}
So you need to use
for number in 0..<(numbers.count-1) {}
The error is because number is not an index, but the element of the array on each iteration. You can modify your code like this:
var numbers = [1,2,1,0,3]
for number in 0..<numbers.count - 1 {
if numbers[number] < numbers[number + 1] {
print(numbers[number])
}
}
Or there is a trick using the sort method, but that's kind of a hack (and yes, the subindexes are right, but look like inverted; you can try this directly on a Playground):
var numbers = [1,2,1,0,3]
numbers.sort {
if $0.1 < $0.0 {
print ($0.1)
}
return false
}
For me, this error occurred when I tried writing a for loop, not for an array but a single element of the array.
For example:
let array = [1,2,3,4]
let item = array[0]
for its in item
{
print(its)
}
This gives an error like: Type Int does not conform to protocol 'sequence'
So, if you get this error in for loop, please check whether you are looping an array or not.

Swift - speed and efficiency of higher order functions (reduce)

Quick question please about the efficiency of higher order swift functions with large input data. During a recent test I had a question about finding 'equlibirum indexes' in arrays- i.e. the index of an array where the sum of all elements below the index equals the sum of all elements above the index
An equilibrium index of this array is any integer P such that 0 ≤ P <
N and the sum of elements of lower indices is equal to the sum of
elements of higher indices, i.e.
A[0] + A[1] + ... + A[P−1] = A[P+1] + ... + A[N−2] + A[N−1].
The challenge was to write a short function which computed the first (or any) index which was considered 'equilibirum'.
I put together a simple snippet which scored highly but failed some of the 'performance' tests which used large input data (array sizes around 100,000).
Here's the code
public func solution(inout A : [Int]) -> Int {
var index = 0;
for _ in A {
let sumBefore = A[0...index].reduce(0) { $0 + $1 }
let sumAfter = A[index...A.count-1].reduce(0) { $0 + $1 }
if (sumBefore == sumAfter) { return index; }
index += 1;
}
return -1;
}
Would anyone please be able to explain why the code performs so poorly with large sets of data, or any recommended alternatives?
Here, for example is a description of a failing perfomance test:
Large performance test, O(n^2) solutions should fail.
✘ TIMEOUT ERROR
running time: >6.00 sec., time limit: 0.22 sec.
It looks like the challenge is failing because your solution is O(n^2).
Your for loop, along with 2 sequential reduces inside, make your solution ~ O(2*n^2) since reduce goes through all the elements again.
A simpler solution is to first compute the whole sum, and then iterate through the elements once, subtracting each value from the whole sum, one by one, thus having access to the left and right sums, for comparison.
Using Swift 3.0, Xcode 8:
func findEquilibriumIndex(in array: [Int]) -> Int? {
var leftSum = 0
var rightSum = array.reduce(0, combine: +)
for (index, value) in array.enumerated() {
rightSum -= value
if leftSum == rightSum {
return index
}
leftSum += value
}
return nil
}
let sampleArray = [-7, 1, 5, 2, -4, 3, 0]
findEquilibriumIndex(in: sampleArray)
The problem is not that "the built-in functions perform so poorly."
Your solution is slow because in each iteration, N elements are
added (N being the length of the array). It would be more efficient
to compute the total sum once and update the "before sum"
and "after sum" while traversing through the array. This reduces
the complexity from O(N^2) to O(N):
public func solution(A : [Int]) -> Int {
var sumBefore = 0
var sumAfter = A.reduce(0, combine: +)
for (idx, elem) in A.enumerate() {
sumAfter -= elem
if sumBefore == sumAfter {
return idx
}
sumBefore += elem
}
return -1
}
(Swift 2.2, Xcode 7.3.1)
Remarks:
There is no reason to pass the array as inout parameter.
An operator (in this case +) can be passed as a argument to the reduce() function.
enumerate() returns a sequence of array indices together with
the corresponding element, this saves another array access.
Note also that a more "Swifty" design would be to make the return type
an optional Int? which is nil if no solution was found.
The incrementalSums extension
If you define this extension
extension Array where Element : SignedInteger {
var incrementalSums: [Element] {
return Array(reduce([0]) { $0.0 + [$0.0.last! + $0.1] }.dropLast())
}
}
given an array of Int(s) you can build an array where the Int at the n-th position represents the sum of the values from 0 to (n-1) in the original array.
Example
[1, 2, 3, 10, 2].incrementalSums // [0, 1, 3, 6, 16]
The equilibriumIndex function
Now you can build a function like this
func equilibriumIndex(nums: [Int]) -> Int? {
let leftSums = nums.incrementalSums
let rightSums = nums.reversed().incrementalSums.reversed()
return Array(zip(leftSums, rightSums)).index { $0 == $1 }
}
Here is a functional version of the solution in Swift 3
let total = sampleArray.reduce(0,+)
var sum = 0
let index = sampleArray.index{ v in defer {sum += v}; return sum * 2 == total - v }
If I understand correctly the element at the resulting index is excluded from the sum on each side (which I'm not certain the other solutions achieve)

How do I pick the nearest element in an array in Swift?

I was surprised I could not find a thread on this, but I need to check a series of arrays for a specific value, and if not present, check if the value falls between the max and min value, and then choose the closest, most negative value to assign to a variable.
I attempted to accomplish this with the function below, but it yields a compiler error: Cannot call value of non-function type "Float!"
Is there any way to overcome the compiler error, or should I try a different approach?
func nearestElement(powerD : Float, array : [Float]) -> Float {
var n = 0
var nearestElement : Float!
while array[n] <= powerD {
n++;
}
nearestElement = array[n] // error: Cannot call value of non-function type "Float!"
return nearestElement;
}
I'd like to then call nearestElement() when I check each array, within arrayContains():
func arrayContains(array: [Float], powerD : Float) {
var nearestElement : Float!
if array.minElement() < powerD && powerD < array.maxElement() {
if array.contains(powerD) {
contactLensSpherePower = vertexedSpherePower
} else {
contactLensSpherePower = nearestElement(powerD, array)
}
}
}
Is there any way to overcome the compiler error, or should I try a different approach?
First, it's worth noting the behavior is largely dependent upon the version of Swift you're using.
In general though, your issue is with naming a variable the same as a method:
func nearestElement(powerD : Float, array : [Float]) -> Float {
var n = 0
var nearestElement : Float! //<-- this has the same name as the function
while array[n] <= powerD {
n++;
}
nearestElement = array[n] // error: Cannot call value of non-function type "Float!"
return nearestElement;
}
Also, in arrayContains, you'll also want to rename var nearestElement : Float! so there's no ambiguity there as well.
Optimised solution using higher order functions:
func closestMatch(values: [Int64], inputValue: Int64) -> Int64? {
return (values.reduce(values[0]) { abs($0-inputValue) < abs($1-inputValue) ? $0 : $1 })
}
Swift is advancing with every version to optimise the performance and efficiency. With higher order functions finding the closest match in an array of values is much easier with this implementation. Change the type of value as per your need.

Resources