I have an array of int I'd like to create a function which retrieves from this array the closest value to randomInt
final myArray = [10 , 20, 14, 15, 18, 24];
final int randomInt = 21;
getClosestValueInArray(myArray, randomInt); // should return 20
int getClosestValueInArray(List<int> array, int value) {
}
You could loop through the array and check closeness for each element. E.g. in pseudo-code:
closest = myArray[0]
best_closeness = abs(myArray[0] - randomInt)
for (element in myArray) {
closeness = abs(element - randomInt)
if (closeness < best_closeness) {
closest = element
best_closeness = closeness
}
}
return closest
And in Dart (added by #julemand101)
int getClosestValueInArray(List<int> array, int value) {
int closest = array.first;
int best_closeness = (closest - value).abs();
for (int element in array.skip(1)) {
int closeness = (element - value).abs();
if (closeness < best_closeness) {
closest = element;
best_closeness = closeness;
}
}
return closest;
}
One option is to put these elements in a TreeSet and then use ts.floor(randomInt) and ts.ceil(randomInt) to check what the nearest elements to randomInt is, and pick the one with the smallest absolute difference to randomInt.
Another option is to put these values in a hashset and then search for randomInt - 1, randomInt + 1, randomInt - 2 and so on. This again has benefits and drawbacks compared to the previous approach.
There are many other ways.
Related
I am trying to access an array in a function, as far as I understand there is no Arraylists in Swift, so I am trying to use regular list:
func findNearsetPoints(pointsArray: [Point] , myPoint: Point )-> Array <Point>{
var twoPoint = [Point]()
var minDist1:Double = DBL_MAX;
var minDist2:Double = DBL_MAX;
var distance:Double = 0
for element in pointsArray{
distance = getDistanceBetweenTwoPoints(pointsArray[element], Point); //error 0
if (distance < minDist1) {
minDist1 = distance;
twoPoint[1] = twoPoint[0];
twoPoint[0] = pointsArray[element]; // error 1
}
else if (distance < minDist2) {
minDist2 = distance;
twoPoint[1] = pointsArray[element]; //error 1
}
}
return twoPoint;
}
func getDistanceBetweenTwoPoints(point1:Point , point2:Point )->Double{
return 5; //TODO
}
error 0:
/Users/user/Desktop/proj/ViewController.swift:145:38: Cannot subscript a value of type '[ViewController.Point]' with an index of type 'ViewController.Point'
error 1:
/Users/user/Desktop/proj/ViewController.swift:149:38: Cannot subscript a value of type '[ViewController.Point]' with an index of type 'ViewController.Point'
What is wrong with the code?
thanks!
your element is already a Point and not an index.
for element in pointsArray{
distance = getDistanceBetweenTwoPoints(point1: element, point2: myPoint) // fix here
if (distance < minDist1) {
minDist1 = distance;
twoPoint[1] = twoPoint[0];
twoPoint[0] = element; // fix here
}
else if (distance < minDist2) {
minDist2 = distance;
twoPoint[1] = element; // fix here
}
}
PS:
take a loook also to this question "Sort array by calculated distance in Swift" for better calculation of distance. just sort the array by distance and then take the first from the array after it is sorted. thats more easy to do
You declared getDistanceBetweenTwoPoints as taking named parameters.
With the current declaration, you need to call it using this syntax:
getDistanceBetweenTwoPoints(point1: aPoint, point2: anotherPoint)
If you want to call it without labels on the parameters then you should redefine it like this:
func getDistanceBetweenTwoPoints(
_ point1:Point ,
_ point2:Point )->Double{
return 5; //TODO
}
I'm not sure if there is an issue or not, so i'm just gonna write it down.
I'm developing using swift, xcode 7.2 , on iphone 5s.
And calculating execution time using
NSDate.timeIntervalSinceReferenceDate()
I created 2 arrays, one with 200,000 elements and one with 20.
and try to have random access to their elements. accessing elements on big one is almost 55 times slower! i know its bigger but isn't this O(1) ?
I also tried the same on java and the accessing speed is the same for big and small array.
From CFArrayheader in apple documentation, i found this:
Accessing any value at a particular index in an array is at worst O(log n), but should usually be O(1).
but it think this cant be true based on the numbers i've tested.
I know i didn't make a big test or anything special, but the fact that its not working is really messing with my head!
i kinda need this for what i'm working on. and the algorithm is not working on swift and iOS and its working on java and android.
let bigSize:Int = 200000
var bigArray = [Int](count:bigSize,repeatedValue:0)
let smallSize:Int = 20
var smallArray = [Int](count:smallSize,repeatedValue:0)
for i in 0..<bigSize
{
bigArray[i] = i + 8 * i
}
for i in 0..<smallSize
{
smallArray[i] = i + 9 * i
}
let indexBig = Int(arc4random_uniform(UInt32(bigSize)) % UInt32(bigSize))
let indexSmall = Int(arc4random_uniform(UInt32(smallSize)) % UInt32(smallSize))
var a = NSDate.timeIntervalSinceReferenceDate()
print(bigArray[indexBig])
var b = NSDate.timeIntervalSinceReferenceDate()
print(b-a) \\prints 0.000888049602508545
a = NSDate.timeIntervalSinceReferenceDate()
print(smallArray[indexSmall])
b = NSDate.timeIntervalSinceReferenceDate()
print(b-a) \\prints 6.90221786499023e-05
java :
(accessing one element is so fast on java and its on pc, so i access more elements, but same number on both arrays)
int bigSize = 200000;
int[] bigArray = new int[bigSize];
Random rand = new Random();
int smallSize = 20;
int[] smallArray = new int[smallSize];
for(int i = 0;i < bigSize;i++)
bigArray[i] = i + i * 8;
for(int i = 0;i < smallSize;i++)
smallArray[i] = i + i * 8;
int smallIndex = rand.nextInt(smallSize);
int bigIndex = rand.nextInt(bigSize);
int sum = 0;
long a = System.currentTimeMillis();
for(int i = 0;i < 10000;i++)
{
sum += bigArray[rand.nextInt(bigSize)];
}
System.out.println(sum);
long b = System.currentTimeMillis();
System.out.println(b-a); //prints 2
a = System.currentTimeMillis();
sum = 0;
for(int i = 0; i < 10000;i++)
{
sum += smallArray[rand.nextInt(smallSize)];
}
System.out.println(sum);
b = System.currentTimeMillis();
System.out.println(b - a); //prints 1
If you change the order of your two tests, you'll find that the performance is flipped. In short, the first test runs more slowly than the second one, regardless of whether it's the small array or the big one. This is a result of some dynamics of print. If you do a print before you perform the tests, the delay resulting from the first print is eliminated.
A better way to test this would be to create a unit test, which (a) repeats the subscript operator many times; and (b) uses measureBlock to repeat the test a few times to check for standard deviation and the like.
When I do that, I find the access time is indistinguishable, consistent with O(1). This were my unit tests:
let bigSize: Int = 200_000
let smallSize: Int = 20
func testBigArrayPerformance() {
let size = bigSize
let array = Array(0 ..< size).map { $0 + 8 * $0 }
var value = 0
measureBlock {
let baseIndex = Int(arc4random_uniform(UInt32(size)))
for index in 0 ..< 1_000_000 {
value += array[(baseIndex + index) % size]
}
}
print(value)
print(array.count)
}
func testSmallArrayPerformance() {
let size = smallSize
let array = Array(0 ..< size).map { $0 + 8 * $0 }
var value = 0
measureBlock {
let baseIndex = Int(arc4random_uniform(UInt32(size)))
for index in 0 ..< 1_000_000 {
value += array[(baseIndex + index) % size]
}
}
print(value)
print(array.count)
}
Admittedly, I've added some mathematical operations that change the index (my intent was to make sure the compiler didn't do some radical optimization that removed my attempt to repeat the subscript operation), and the overhead of that mathematical operation will dilute the subscript operator performance difference. But, even when I simplified the index operator, the performance between the two renditions was indistinguishable.
var mentions = ["#alex", "#jason", "#jessica", "#john"]
I want to limit my array to 3 items, so I want to splice it:
var slice = [String]()
if mentions.count > 3 {
slice = mentions[0...3] //alex, jason, jessica
} else {
slice = mentions
}
However, I'm getting:
Ambiguous subscript with base type '[String]' and index type 'Range'
Apple Swift version 2.2 (swiftlang-703.0.18.8 clang-703.0.31)
Target: x86_64-apple-macosx10.9
The problem is that mentions[0...3] returns an ArraySlice<String>, not an Array<String>. Therefore you could first use the Array(_:) initialiser in order to convert the slice into an array:
let first3Elements : [String] // An Array of up to the first 3 elements.
if mentions.count >= 3 {
first3Elements = Array(mentions[0 ..< 3])
} else {
first3Elements = mentions
}
Or if you want to use an ArraySlice (they are useful for intermediate computations, as they present a 'view' onto the original array, but are not designed for long term storage), you could subscript mentions with the full range of indices in your else:
let slice : ArraySlice<String> // An ArraySlice of up to the first 3 elements
if mentions.count >= 3 {
slice = mentions[0 ..< 3]
} else {
slice = mentions[mentions.indices] // in Swift 4: slice = mentions[...]
}
Although the simplest solution by far would be just to use the prefix(_:) method, which will return an ArraySlice of the first n elements, or a slice of the entire array if n exceeds the array count:
let slice = mentions.prefix(3) // ArraySlice of up to the first 3 elements
We can do like this,
let arr = [10,20,30,40,50]
let slicedArray = arr[1...3]
if you want to convert sliced array to normal array,
let arrayOfInts = Array(slicedArray)
You can try .prefix().
Returns a subsequence, up to the specified maximum length, containing the initial elements of the collection.
If the maximum length exceeds the number of elements in the collection, the result contains all the elements in the collection.
let numbers = [1, 2, 3, 4, 5]
print(numbers.prefix(2)) // Prints "[1, 2]"
print(numbers.prefix(10)) // Prints "[1, 2, 3, 4, 5]"
General solution:
extension Array {
func slice(size: Int) -> [[Element]] {
(0...(count / size)).map{Array(self[($0 * size)..<(Swift.min($0 * size + size, count))])}
}
}
Can also look at dropLast() function:
var mentions:[String] = ["#alex", "#jason", "#jessica", "#john"]
var slice:[String] = mentions
if mentions.count > 3 {
slice = Array(mentions.dropLast(mentions.count - 3))
}
//print(slice) => ["#alex", "#jason", "#jessica"]
I came up with this:
public extension Array {
func slice(count: Int) -> [some Collection] {
let n = self.count / count // quotient
let i = n * count // index
let r = self.count % count // remainder
let slices = (0..<n).map { $0 * count }.map { self[$0 ..< $0 + count] }
return (r > 0) ? slices + [self[i..<i + r]] : slices
}
}
You can also slice like this:
//Generic Method
func slice<T>(arrayList:[T], limit:Int) -> [T]{
return Array(arrayList[..<limit])
}
//How to Use
let firstThreeElements = slice(arrayList: ["#alex", "#jason", "#jessica", "#john"], limit: 3)
Array slice func extension:
extension Array {
func slice(with sliceSize: Int) -> [[Element]] {
guard self.count > 0 else { return [] }
var range = self.count / sliceSize
if self.count.isMultiple(of: sliceSize) {
range -= 1
}
return (0...range).map { Array(self[($0 * sliceSize)..<(Swift.min(($0 + 1) * sliceSize, self.count))]) }
}
}
I am making a game using Swift and SpriteKit where i move an object to random locations based on an array.
The array that is made up of CGPoints:
let easyArray = [CGPointMake(0,0), CGPointMake(126.6,0), CGPointMake(253.4,0), CGPointMake(0,197.5), CGPointMake(126.7,197.5), CGPointMake(253.4,197.5), CGPointMake(0,395), CGPointMake(126.7,395), CGPointMake(253.4,395)]
I use this function to generate a random number:
func randomNumber(maximum: UInt32) -> Int {
var randomNumber = arc4random_uniform(maximum)
while previousNumber == randomNumber {
randomNumber = arc4random_uniform(maximum)
}
previousNumber = randomNumber
return Int(randomNumber)
}
I used this to move the object based on the random number generated:
let greenEasy = randomNumberNew(9)
let moveSelector = SKAction.moveTo(easyArray[greenEasy], duration: 0)
selector.runAction(moveSelector)
I have done some reading online and found that the "While" condition should make it so that the same random number isn't generate twice in a row. But it still happens.
Can anyone please help me on how to make it so i don't get the same number twice in a row?
The code below doesn't random the same number.
var currentNo: UInt32 = 0
func randomNumber(maximum: UInt32) -> Int {
var randomNumber: UInt32
do {
randomNumber = (arc4random_uniform(maximum))
}while currentNo == randomNumber
currentNo = randomNumber
return Int(randomNumber)
}
I think Larme's suggestion is pretty clever, actually.
easyArray.append(easyArray.removeAtIndex(Int(arc4random_uniform(UInt32(easyArray.count)-1))))
selector.runAction(SKAction.moveTo(easyArray.last!, duration: 0))
I would recommend to not use while() loops with randomizers.
Theoretically it can cause infinite loops in worst case scenario, in more positive scenario it will just take few loops before you get desired results.
Instead I would advice to make an NSArray of all values, remove from this NSArray last randomized element and randomize any of other existing elements from such an array - that is guarantee result after only one randomize iteration.
It can be easily achieved by making NSArray category in Objective-C:
- (id) randomARC4Element
{
if(self.count > 0)
{
return [self objectAtIndex:[self randomIntBetweenMin:0 andMax:self.count-1]];
}
return nil;
}
- (int)randomIntBetweenMin:(int)minValue andMax:(int)maxValue
{
return (int)(minValue + [self randomFloat] * (maxValue - minValue));
}
- (float)randomFloat
{
return (float) arc4random() / UINT_MAX;
}
If you can use linq then you can select a random value that doesn't match the last value. Then for any left over values you can loop through and find valid places to insert them.
It's not the most efficient but it works.
public static List<int> Randomize(List<int> reps, int lastId) {
var rand = new Random();
var newReps = new List<int>();
var tempReps = new List<int>();
tempReps.AddRange(reps);
while (tempReps.Any(x => x != lastId)) {
var validReps = tempReps.FindAll(x => x != lastId);
var i = rand.Next(0, validReps.Count - 1);
newReps.Add(validReps[i]);
lastId = validReps[i];
tempReps.Remove(validReps[i]);
}
while (tempReps.Any()) {
var tempRep = tempReps.First();
bool placed = false;
for (int i = 0; i < newReps.Count; i++) {
if (newReps[i] == tempRep) {
continue;
}
else if ((i < newReps.Count - 1) && (newReps[i + 1] == tempRep)) {
continue;
}
else {
newReps.Insert(i + 1, tempRep);
placed = true;
break;
}
}
if (placed) {
tempReps.Remove(tempRep);
}
else {
throw new Exception("Unable to randomize reps");
}
}
return newReps;
}
I'd like to generate multiple different random numbers in Swift. Here is the procedure.
Set up an empty array
Generate a random number
Check if the array is empty
a. If the array is empty, insert the random number
b. If the array is not empty, compare the random number to the numbers in array
i. If the numbers are the same, repeat 2
ii. if the numbers are not the same, insert the random number and repeat 2
import UIKit
//the random number generator
func randomInt(min: Int, max:Int) -> Int {
return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}
var temp = [Int]()
for var i = 0; i<4; i++ {
var randomNumber = randomInt(1, 5)
if temp.isEmpty{
temp.append(randomNumber)
} else {
//I don't know how to continue...
}
}
If you use your method the problem is, that you will create a new random-number each time. So you possibly could have the same random-number 4 times and so your array will only have one element.
So, if you just want to have an array of numbers from within a specific range of numbers (for example 0-100), in a random order, you can first fill an array with numbers in 'normal' order. For example with for loop etc:
var min = 1
var max = 5
for var i = min; i<= max; i++ {
temp.append(i)
}
After that, you can use a shuffle method to shuffle all elements of the array with the shuffle method from this answer:
func shuffle<C: MutableCollectionType where C.Index == Int>(var list: C) -> C {
let count = countElements(list)
for i in 0..<(count - 1) {
let j = Int(arc4random_uniform(UInt32(count - i))) + i
swap(&list[i], &list[j])
}
return list
}
Ater that you can do something like that:
shuffle(temp) // e.g., [3, 1, 2, 4, 5]
The construct you’re looking for with your approach might be something like:
var temp: [Int] = []
while temp.count < 4 {
var randomNumber: Int
do {
randomNumber = randomInt(1, 5)
} while contains(temp, randomNumber)
temp.append(randomNumber)
}
This will be fine for tiny ranges like yours, but for larger ranges it will be very slow, because for the last few numbers you are waiting for the random number to hit precisely the remaining handful of possibilities. I just tried generating from a range of 200 in a playground and it took 9 seconds.
If you want a random selection of numbers with guaranteed coverage over a range, you could generate it like by taking that range and shuffling it, like this:
func shuffle<S: SequenceType>(source: S) -> [S.Generator.Element] {
var rangen = GeneratorOf { arc4random() }
let a = Array(Zip2(rangen, source))
return a.sorted { $0.0 < $1.0 }.map { $0.1 }
}
let min = 1, max = 5
shuffle(min...max)
If you want a selection of n non-repeating random numbers from a range 0..<m, there’s a particularly pretty algorithm to do this that generates an ascending sequence of random numbers from that range:
func randomGeneratorOf(#n: Int, #from: Int) -> GeneratorOf<Int> {
var select = UInt32(n)
var remaining = UInt32(from)
var i = 0
return GeneratorOf {
while i < from {
if arc4random_uniform(remaining) < select {
--select
--remaining
return i++
}
else {
--remaining
++i
}
}
return nil
}
}
Which you could use like so:
let engines = [
"Duck","Emily","Gordon","Henry", "Mavis",
"Belle","James","Edward","Thomas","Toby"
]
let picks = Array(randomGeneratorOf(n: 3, from: engines.count))
for engine in PermutationGenerator(elements: engines, indices: picks) {
println(engine)
}
Below is my suggestion.
I like this way since it is short and simple :)
let totalCount: Int = 150 //Any number you asssign
var randomNumArray: [Int] = []
var i = 0
while randomNumArray.count < totalCount {
i++
let rand = Int(arc4random_uniform(UInt32(totalCount)))
for(var ii = 0; ii < totalCount; ii++){
if randomNumArray.contains(rand){
print("do nothing")
} else {
randomNumArray.append(rand)
}
}
}