NSArray -> Find closest value - fastest way - objective-c

Lets say I've got an ordered NSArray of NSNumbers:
2, 4, 8, 15, 16, 20 // for simplicity let's treat it as array of int's instead of NSNumbers
Now I need to find closest index to let's say value == 19.
searchValue = 19;
minIndex = 0;
maxIndex = array.count - 1;
currentIndex = (int)floorf(maxIndex / 2.f);
while (maxIndex - minIndex == 1) {
if (array[currentIndex] < searchValue) { // go right
minIndex = currentIndex;
} else if (array[currentIndex] > searchValue) { // go left
maxIndex = currentIndex;
} else { // exact value, rather low probability of happening
return currentIndex;
}
currentIndex = (int)floorf((maxIndex - minIndex) / 2.f);
}
// let's check values around, who has smaller difference
int leftDifference = (currentIndex - 1 >= 0) ? abs(array[currentIndex - 1] - searchValue) : INT_MAX;
int rightDifference = (currentIndex + 1 < array.count) ? abs(array[currentIndex + 1] - searchValue) : INT_MAX;
int centralDifference = abs(array[currentIndex] - searchValue);
if (leftDifference < rightDifference && leftDifference < centralDifference) {
return currentIndex - 1;
} else if () {
return currentIndex + 1;
} else {
return currentIndex;
}
This is the fastest way I can imagine, maybe someone has different idea? How can I improve the algorithm?
I've took a look into egSOF question, but it search for value not index and does it by browsing all values. In case of index, we don't have to browse full array.

Lets assume you have an array of NSNumbers:
NSArray *array = #[#(2), #(4), #(8), #(15), #(16), #(20)];
And you are looking for myValue as below:
NSNumber *myValue = #(17);
Use indexOfObject:inSortedRange:options:usingComparator method to find the nearest index of array to you value. Binary search has O(log n) performance so is pretty fast.
NSInteger searchIndex = MIN([array indexOfObject: myValue inSortedRange:NSMakeRange(0, array.count)
options:NSBinarySearchingFirstEqual | NSBinarySearchingInsertionIndex
usingComparator:^NSComparisonResult(NSNumber *obj1, NSNumber *obj2) {
return [obj1 compare:obj2];
}], [array count] - 1);
Then check if exists a number closest to yours myValue on the searchIndex - 1 index:
if (searchIndex > 0) {
CGFloat leftHandDiff = ABS(((NSNumber *)array[searchIndex - 1]).floatValue - myValue.floatValue);
CGFloat rightHandDiff = ABS(((NSNumber *)array[searchIndex]).floatValue - myValue.floatValue);
if (leftHandDiff == rightHandDiff) {
//here you can add behaviour when your value is in the middle of range
NSLog(#"given value is in the middle");
} else if (leftHandDiff < rightHandDiff) {
searchIndex--;
}
}
NSLog(#"The nearest value to %f is %d in array at index %d", myValue.floatValue, array[searchIndex], searchIndex);
and voila! Now you now the closest value to myValue.
Remember that your array has to be sorted ascending to make this trick.

Related

Make both sorting algorithms put the values in descending order. Then create drive class to test the two algorithms

I am extremely confused on how to reverse these sorting methods. Any help would be appreciated.
I have looked up and tried researching but I could not find anything to do with this type of comparable list.
public class Sorting
{
public static void selectionSort(Comparable[] list)
{
int min;
Comparable temp;
for (int index = 0; index < list.length-1; index++)
{
min = index;
for (int scan = index+1; scan < list.length; scan++)
if (list[scan].compareTo(list[min]) < 0)
min = scan;
temp = list[min];
list[min] = list[index];
list[index] = temp;
}
}
public static void insertionSort(Comparable[] list)
{
for (int index = 1; index < list.length; index++)
{
Comparable key = list[index];
int position = index;
while (position > 0 && key.compareTo(list[position-1]) < 0)
{
list[position] = list[position-1];
position--;
}
list[position] = key;
}
}
}
I think if you change:
if (list[scan].compareTo(list[min]) < 0)
to
if (list[scan].compareTo(list[min]) > 0)
it will sort in reverse order.
Here is the api
int compareTo(T o)
Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.

Time/Space-Complexity method

I got a question to answer with the best complexity we can think about.
We got one sorted array (int) and X value. All we need to do is to find how many places in the array equals the X value.
This is my solution to this situation, as i don't know much about complexity.
All i know is that better methods are without for loops :X
class Question
{
public static int mount (int [] a, int x)
{
int first=0, last=a.length-1, count=0, pointer=0;
boolean found=false, finish=false;
if (x < a[0] || x > a[a.length-1])
return 0;
while (! found) **//Searching any place in the array that equals to x value;**
{
if ( a[(first+last)/2] > x)
last = (first+last)/2;
else if ( a[(first+last)/2] < x)
first = (first+last)/2;
else
{
pointer = (first+last)/2;
count = 1;
found = true; break;
}
if (Math.abs(last-first) == 1)
{
if (a[first] == x)
{
pointer = first;
count = 1;
found = true;
}
else if (a[last] == x)
{
pointer = last;
count = 1;
found = true;
}
else
return 0;
}
if (first == last)
{
if (a[first] == x)
{
pointer = first;
count = 1;
found = true;
}
else
return 0;
}
}
int backPointer=pointer, forwardPointer=pointer;
boolean stop1=false, stop2= false;
while (!finish) **//Counting the number of places the X value is near our pointer.**
{
if (backPointer-1 >= 0)
if (a[backPointer-1] == x)
{
count++;
backPointer--;
}
else
stop1 = true;
if (forwardPointer+1 <= a.length-1)
if (a[forwardPointer+1] == x)
{
count++;
forwardPointer++;
}
else
stop2 = true;
if (stop1 && stop2)
finish=true;
}
return count;
}
public static void main (String [] args)
{
int [] a = {-25,0,5,11,11,99};
System.out.println(mount(a, 11));
}
}
The print command count it right and prints "2".
I just want to know if anyone can think about better complexity for this method.
Moreover, how can i know what is the time/space-complexity of the method?
All i know about time/space-complexity is that for loop is O(n). I don't know how to calculate my method complexity.
Thank a lot!
Editing:
This is the second while loop after changing:
while (!stop1 || !stop2) //Counting the number of places the X value is near our pointer.
{
if (!stop1)
{
if ( a[last] == x )
{
stop1 = true;
count += (last-pointer);
}
else if ( a[(last+forwardPointer)/2] == x )
{
if (last-forwardPointer == 1)
{
stop1 = true;
count += (forwardPointer-pointer);
}
else
forwardPointer = (last + forwardPointer) / 2;
}
else
last = ((last + forwardPointer) / 2) - 1;
}
if (!stop2)
{
if (a[first] == x)
{
stop2 = true;
count += (pointer - first);
}
else if ( a[(first+backPointer)/2] == x )
{
if (backPointer - first == 1)
{
stop2 = true;
count += (pointer-backPointer);
}
else
backPointer = (first + backPointer) / 2;
}
else
first = ((first + backPointer) / 2) + 1;
}
}
What do you think about the changing? I think it would change the time complexity to O(long(n)).
First let's examine your code:
The code could be heavily refactored and cleaned (which would also result in more efficient implementation, yet without improving time or space complexity), but the algorithm itself is pretty good.
What it does is use standard binary search to find an item with the required value, then scans to the back and to the front to find all other occurrences of the value.
In terms of time complexity, the algorithm is O(N). The worst case is when the entire array is the same value and you end up iterating all of it in the 2nd phase (the binary search will only take 1 iteration). Space complexity is O(1). The memory usage (space) is unaffected by growth in input size.
You could improve the worst case time complexity if you keep using binary search on the 2 sub-arrays (back & front) and increase the "match range" logarithmically this way. The time complexity will become O(log(N)). Space complexity will remain O(1) for the same reason as before.
However, the average complexity for a real-world scenario (where the array contains various values) would be very close and might even lean towards your own version.

Mapping elements in an NSArray to their counts and getting the most frequent

I am working on presentation where I am comparing collections and algorithms of standard libraries of different languages. Help me to write effective and readable algorithm of this problem:
Metaprogramming language:
struct Tweet { id, time, text, url, user_id };
struct User { id, name, nick };
array<Tweet> tweets;
map<int, User> userDict;
Problem:
Find the user name who sent the greatest number of tweets with url field not equal to nil.
Also find the count of such tweets from this user.
I have started like this:
NSMutableDictionary * countByUserId = [NSMutableDictionary dictionary];
foreach (Tweet * tweet in tweets)
if (tweet.url != 0)
countByUserId[tweet->user_id] = #(countByUserId[tweet->user_id] + 1);
Now I need to find key-value with max value.
This is my C++ code:
map<int,int> countByUserId;
for (auto it = tweets.begin(); it != tweets.end(); ++it)
if (it->url != 0)
countByUserId[it->user_id]++;
auto para = max_element(countByUserId.begin(),
countByUserId.end(),
[](pair<int,int> a, pair<int,int> b)
{ return a.second < b.second; });
cout << "User = " << userDict[para.first].name << endl
<< "Value = " << para.second << endl;
Now I need to find key-value with max value...
There may be ties, so you should look for all key-value pairs with the max value. You can compute the max in your loop, and then walk through the NSDictionary again, looking for it:
NSMutableDictionary * countByUserId = [NSMutableDictionary dictionary];
NSInteger max = -1;
foreach (Tweet * tweet in tweets) {
if (tweet.url != 0) {
int next = countByUserId[tweet->user_id].intValue + 1;
countByUserId[tweet->user_id] = #(next);
if (next > max) {
max = next;
}
}
}
[countByUserId enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if ([obj intValue] == max) {
NSLog(#"Found max for user: %#", key);
}
}

Is it possible to iterate a masked enum in objective c

I've seen that this is possible in other languages but need something like this in objective-c
I have an enum similar to this
typedef enum {
option1 = 1 << 0,
option2 = 1 << 1,
option3 = 1 << 2
...
...
} SomePossibleOptions;
and then a user can create a mask of the wanted options
SomePossibleOptions myOptions = option1 | option2;
[self.someObject performOperationsForOptions:myOptions];
-(void)performOperationsForOptions:(SomePossibleOptions)theOptions
{
if (myOptions & option1)
{
// do something
}
if (myOptions & option2
{
// do something
}
//(could use a switch statement)
}
But would much rather use some sort of syntax
foreach (option in myoption)
{
//do something
}
Sometimes I use a last value in my normal enums called "SomeEnumCount", which then has exactly the number of items I have in the enum, so I can make a loop for that.
In your case it would be something like this:
typedef enum {
option1 = 1 << 0,
option2 = 1 << 1,
option3 = 1 << 2,
...
...
optionCount = 1 << n
} SomePossibleOptions;
Or maybe you can call it OptionNone, if you have one like that, and that would be always the last one.
And to make a loop you have to make something like this
NSInteger optionsCount = (int)log2(optionCount);
for (NSInteger i = 0; i < optionsCount; i++) {
SomePossibleOptions option = (SomePossibleOptions)(1 << i);
//handle your options here
}
I hope it helps!
EDIT: Maybe I misunderstood the question. If you want to loop on just the options that are masked together, you should write a function, based on the above. Something like:
- (NSArray *)optionsInMask:(SomePossibleOptions)maskedOptions {
NSMutableArray * options = [NSMutableArray array];
NSInteger optionsCount = (int)log2(optionCount);
for (NSInteger i = 0; i < optionsCount; i++) {
SomePossibleOptions option = (SomePossibleOptions)(1 << i);
if (maskedOptions & option) {
[options addObject:[NSValue valueWithInteger:option]];
}
}
return [NSArray arrayWithArray:options];
}
And then you can loop it like:
for (NSValue * value in [self optionsInMask:myOptions]) {
SomePossibleOption option = (SomePossibleOptions)[value integerValue];
//your code here
}

Performing multiple comparisons efficiently

I have a problem with my Objective-C code. I would like to have an if statement in a formula saying that the condition is checked just as if a float is equal to one of the other 5 I have defined. But shorter and simpler than:
if (float1 == float5 || float1 == float2 || float1 == float3 || float1 == float11)
{
//something to do
}
Thank you very much for your answers.
Assuming that you can guarantee that the float is an integer, either by definition or applying roundf(), floorf(), ceilf(), this is a possible solution:
const NSUInteger LENGTH = 7;
float myNumbersToCheck[LENGTH] = {1,13,6,8,99,126,459674598};
for (NSUInteger index = 0; index < LENGTH; index++) {
if (float1 == myNumbersToCheck[index]) {
// do something
break;
}
}