undo sorting of nsarray - objective-c
Here is a example of what I want to do with NSArray contains NSNumber.
This is the NSArray "score" I want to edit.
score[0]=30,
score[1]=10,
score[2]=20
score[3]=0
Sort the Array in ascending-order
score[0]=30 //[0]This number shows the index before sorting the array
score[1]=20 //[2]
score[2]=10 //[1]
score[3]=0 //[3]
Edit the Array(In this case,4th gives 1st 10 points,and 3rd gives 2nd 5points)
score[0]=40 //[0]
score[1]=25 //[2]
score[2]=5 //[1]
score[3]=-10 //[3]
And sort the array back they were.
score[0]=40
score[1]=5
score[2]=25
score[3]=-10
I have a problem with no.4 method in the list.Can someone give me some idea with this?
Thanks in advance.
You don't actually need to swap the values themselves, you can set up an extra level of indirection and use that.
Before the sort, you have the indexes initialised to point to the corresponding scores:
index[0] = 0 score[0] = 30
index[1] = 1 score[1] = 10
index[2] = 2 score[2] = 20
index[3] = 3 score[3] = 0
When you sort, you actually sort the indexes based on the scores they point to rather than the scores themselves. So, instead of the following comparison in your sort:
if score[i] > score[i+1] then swap score[i], score[i+1]
you instead use:
if score[index[i]] > score[index[i+1]] then swap index[i], index[i+1]
Following the sort, you then have:
index[0] = 0 score[0] = 30
index[1] = 2 score[1] = 10 \ These two indexes have been swapped
index[2] = 1 score[2] = 20 / but NOT the scores.
index[3] = 3 score[3] = 0
Then, to move points, you use the indirect indexes rather than the direct values:
score[index[0]] += 10; score[index[3]] -= 10;
score[index[1]] += 5; score[index[2]] -= 5;
Then you throw away the indexes altogether, the original array doesn't need restoration to its original order, simply because its order was never changed.
Make an additional array initialized as such:
index[0] = 0
index[1] = 1
index[2] = 2
:
And every time your sorting algorithm swaps two indices in score, you also swap the same indices in index.
In case your sorting algorithm is built-in, so that you have no control over it, you will have to replace each score with a tuple (an object, a two-element array, whichever is easier in objective-c), where on element is the score, and the other element is its original index. When you sort, you would pass a custom comparator to the sorting function, so that only the score is used for comparison. That would sort your score-index tuples, so that you can use the index to restore them to original order.
[Code will be typed in, check it!]
I assume your score array is actually mutable as you intend to alter it:
NSMutableArray *score = ...;
Create another array the same size and initialized to 0..n:
NSMutableArray *indices = [NSMutableArray arrayWithCapacity:[score count]];
// add the numbers 0..[score count] to indices
Now sort the indices array using a custom comparator which looks up the score array:
[indices sortUsingComparator:(NSComparator)^(NSNumber *a, NSNumber *b)
{
return [((NSNumber *)[[score objectAtIndex:[a integerValue]])
compare:[[score objectAtIndex:[b integerValue]]
];
}
]
Now you can modify your original array via the indices array, e.g. to modify the "4th" element after the source:
[score replaceObjectAtIndex:[[indices objectAtIndex:3] integerValue] withObject:...];
Now you don't need to "unsort" score at all, your step 4 is "do nothing".
Related
Objective-C NSIndexSet / NSArray - Selecting the "Best" Index from Set using Standard Dev
I have a question now about using standard deviation. And if I'm using it properly for my case as laid out below. The Indexes are all Unique here's a few questions I have about Standard Deviation: 1) Since I'm using all of the data should I be using a population Standard Dev or should I use a sample Standard Dev? 2) Does it matter what the length (range) of the full playlist is (1...15) I have a program which takes a Playlist of Songs and gets recommendations for each song from Spotify. Say the playlist has a length of 15. Each tracks gets an a array of Suggestions of about 30 tracks. And in the end my program will filter down all of the suggestions to create a new playlist of only 15 tracks. There is often duplicates that get recommended. I have devised a method for finding these duplicates and then putting their index into a NSIndexSet. In my example there is a duplicate track that was suggested for tracks in the original playlist at indexes 4, 6, 7, 12 I'm trying to calculate out which is the best one of the duplicates to pick. All of the NSSet methods etc were not going to help me an would not take into account "where" the duplicates where place. To me it makes sense that the more ofter within a "zone" a track was suggested would make the most sense to "use" it at that location in the final suggested playist. Originally I was just selecting the index closest to the mean (7.25) But to me I would think that 6 would be a better choice than 7. The 12 seems to throw it off. So I started to investigating StdDev and figured that could help me out How do you think my approach to this here is? NSMutableIndexSet* dupeIndexsSet; // contains indexes 4,6,7,12 // I have an extension on NSIndexSet to create a NSArray from it NSArray* dupesIndexSetArray = [dupeIndexsSet indexSetAsArray]; // #[4, 6, 7, 12] NSUInteger dupeIndexsCount = [dupeIndexSetArray count]; // 4 NSUInteger dupeIndexSetFirst = [dupeIndexsSet firstIndex]; // 4 NSUInteger dupeIndexSetLast = [dupeIndexsSet lastIndex]; // 12 // I have an extension on NSArray to calculate the mean NSNumber* dupeIndexsMean = [dupeIndexArray meanOf]; // 7.25; the populationSD is 2.9475 the populationVariance is 8.6875 the sampleSD is 3.4034 the sampleVariance is 11.5833 Which SD should I use? Or will it matter I learned that the SD is the range from the Mean so I figured I would calculate out what those values are. double mean = [dupeIndexsMean doubleValue]; double dev = [populationSD doubleValue]; NSUInteger stdDevRangeStart = MAX(round(mean - dev), dupeIndexSetFirst); // 7.25 - 2.8475 = 4.4025, round 4, dupeIndexSetFirst = 4 // stdDevRangeStart = 4; NSUInteger stdDevRangeEnd = MIN(round(mean + dev), dupeIndexSetLast); // 7.25 + 2.8475 = 10.0975, round 10, dupeIndexSetLast = 12 // stdDevRangeEnd = 10; NSUInteger stdDevRangeLength1 = stdDevRangeEnd - stdDevRangeStart; // 6 NSUInteger stdDevRangeLength2 = MAX(round(dev * 2), stdDevRangeLength1); // 2.8475 * 2 = 5.695, round 6, stdDevRangeLength1 = 6 // stdDevRangeLength2 = 6; NSRange dupeStdDevRange = NSMakeRange(stdDevRangeStart, stdDevRangeLength2); // startIndex = 4, length 6 So I figured if this new range would give me a better range that would include a more accurate stdDev and not include the 12. I create a newIndexSet from the original one that only includes the indexes that are included from my new dupeStdDevRange NSMutableIndexSet* stdDevIndexSet = [NSMutableIndexSet new]; [dupeIndexsSet enumerateIndexesInRange:dupeStdDevRange options:NSEnumerationConcurrent usingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { [stdDevIndexSet addIndex:idx]; }]; // stdDevIndexSet has indexes = 4, 6, 7 the new stdDevIndexSet now includes indexes 4,6,7 12 was not included, which is great cause I thought was throwing everything off now with this new "stdDevIndexSet" I check it against the original IndexSet If the stdDevIndexSet count is less, then I reload this new indexSet into the whole process and calculate everything again. if ([stdDevIndexSet count] < dupeIndexesCount) { [self loadDupesIndexSet:stdDevIndexSet]; } else { doneTrim = YES; } So it is different, so I start the whole process again with index set that includes 4,6,7 updated calculations dupeIndexsMean = 5.6667; populationSD = 1.2472; populationVariance = 1.5556; sampleSD = 1.5275; sampleVariance = 2.3333; stdDevRangeStart = 4; stdDevRangeEnd = 7; The newTrimmed IndexSet now "fits" the Stand Deviation Range. So if I use the new Mean rounded to 6. My Best Index to use is 6 from the original (4, 6, 7, 12) Which now makes send to me. So big question am I approaching this correctly? Do things like the original Size (length) of the "potential" range matter? IE if the original playlist length was 20 tracks as compared to 40 tracks? (I'm thinking not).
C memory management with 2D arrays
I'm starting to play around with some C code within Objective-C programs. The function I'm trying to write sorts all of the lat/long coordinates from a KML file into clusters based on 2D arrays. I'm using three 2D arrays to accomplish this: NSUInteger **bucketCounts refers to the number of CLLocationCoordinate2Ds in a cluster. CLLocationCoorindate2D **coordBuckets is an array of arrays of coordinates NSUInteger **bucketPointers refers to an index in the array of coordinates from coordBuckets Here's the code that is messing me up: //Initialize C arrays and indexes int n = 10; bucketCounts = (NSUInteger**)malloc(sizeof(NSUInteger*)*n); bucketPointers = (NSUInteger**)malloc(sizeof(NSUInteger*)*n); coordBuckets = (CLLocationCoordinate2D **)malloc(sizeof(CLLocationCoordinate2D*)*n); for (int i = 0; i < n; i++) { bucketPointers[i] = malloc(sizeof(NSUInteger)*n); bucketCounts[i] = malloc(sizeof(NSUInteger)*n); } NSUInteger nextEmptyBucketIndex = 0; int bucketMax = 500; Then for each CLLocationCoordinate2D that needs to be added: //find location to enter point in matrix int latIndex = (int)(n * (oneCoord.latitude - minLat)/(maxLat-minLat)); int lonIndex = (int)(n * (oneCoord.longitude - minLon)/(maxLon-minLon)); //see if necessary bucket exists yet. If not, create it. NSUInteger positionInBucket = bucketCounts[latIndex][lonIndex]; if (positionInBucket == 0) { coordBuckets[nextEmptyBucketIndex] = malloc(sizeof(CLLocationCoordinate2D) * bucketMax); bucketPointers[latIndex][lonIndex] = nextEmptyBucketIndex; nextEmptyBucketIndex++; } //Insert point in bucket. NSUInteger bucketIndex = bucketPointers[latIndex][lonIndex]; CLLocationCoordinate2D *bucketForInsert = coordBuckets[bucketIndex]; bucketForInsert[positionInBucket] = oneCoord; bucketCounts[latIndex][lonIndex]++; positionInBucket++; //If bucket is full, expand it. if (positionInBucket % bucketMax == 0) { coordBuckets[bucketIndex] = realloc(coordBuckets[bucketIndex], (sizeof(CLLocationCoordinate2D) * (positionInBucket + bucketMax))); } Things seem to be going well for about 800 coordinates, but at the same point a value in either bucketCounts or bucketPointers gets set to an impossibly high number, which causes a reference to a bad value and crashes the program. I'm sure this is a memory management issue, but I don't know C well enough to troubleshoot it myself. Any helpful pointers for where I'm going wrong? Thanks!
It seems to me each entry in bucketPointers can potentially have its own "bucket", requiring a unique element of coordBuckets to hold the pointer to that bucket. The entries in bucketPointers are indexed by bucketPointers[latIndex][lonIndex], so there can be n*n of them, but you allocated only n places in coordBuckets. I think you should allocate for n*n elements in coordBuckets.
Two problems I see: You don't initialize bucketCounts[] in the given code. It may well happen to all 0s but you should still initialize it with calloc() or memset(): bucketCounts[i] = calloc(n, sizeof(NSUInteger)); if oneCoord.latitude == maxLat then latIndex == n which will overflow your arrays which have valid indexes from 0 to n-1. Same issue with lonIndex. Either allocate n+1 elements and/or make sure latIndex and lonIndex are clamped from 0 to n-1. In code using raw arrays like this you can solve a lot of issues with two simple rules: Initialize all arrays (even if you technically don't need to). Check/verify all array indexes to prevent out-of-bounds accesses.
"Weighted" random number generator
I'm currently using the below code to grab a random element from an array. How would I go about changing the code so that it returns an element weighted on the percentage that I want it to come up? For example, I want the element at index 0 to come up 27.4% of the time, but the element at index 7 to come up only 5.9% of the time. NSArray *quoteArray = #[ #"quote1", #"quote2", #"quote3", #"quote4", #"quote5", #"quote6", #"quote7", #"quote8", ]; NSString *quoteString; int r = arc4random() % [quoteArray count]; if(r<[rewardTypeArray count]) quoteString = [quoteArray objectAtIndex:r];
I would use an array of float (wrapped into NSNumber) objects. Every object represents a percentage.In this case you would have an array of 8 objects: Object 1: #27.5 ; ... Object 7: #5.9 . Then you get a random number from 1 to 100. If you want more precision you can also get a random number with the decimal part, and the precision doesn't influence the efficiency and neither the memory used. Then when you get the number you iterate through all the array, keep track of the index and the percentage that you have. You use a float to sum all the percentages met and you stop only when the total percentage is greater on equal that the one that you have. Example NSArray* percentages= #[ #27.4 , ... , #5.9]; float randomNumber= arc4random_uniform(100) + (float)arc4random_uniform(101)/100; NSUInteger n=0; float totalPercentage= 0.0; for(NSUInteger i=0; i<percentages.count; i++) { totalPercentage+= [ percentages[i] floatValue ]; if( totalPercentage >= randomNumber) // This case we don't care about // the comparison precision { break; } n++; } // Now n is index that you want
The easiest way would be to generate a random number based on how fine-grained you want the percentage to be. To calculate to the tenth of a percent, you could generate between 0-1000, and 274 of the values you could randomly generate would be the first element. 59 values would correspond to element 7. For example: 0-273 = index 1 27.4% 274-301 = index 2 2.7% 302-503 = index 3 20.1% 504-550 = index 4 4.6% 551-700 = index 5 14.9% 701-941 = index 6 24% 942-1000 = index 7 5.9% The percentages don't add up properly, so I did my math wrong somewhere, but you get the point.
You can make another array with counter that would keep tracking how many times each one of your elements is being generated. If that counter is less than your target let that index come in your r, otherwise regenarate.
Changing content of a matrix in c
How do you change just part of a matrix in c (I'm actually in Objective-C, but using matrices in c). For example: NSInteger tempMapMatrix[100][100] = {{0,0,1,1,2,2,1,1,0,2,4,4,4,0,0,1,2,2,1,0,0,0,0,0,0}, {0,1,1,2,3,2,1,1,4,4,3,4,4,0,0,1,2,2,1,0,0,0,0,0,0}, {1,1,2,3,3,2,1,4,1,3,3,4,4,0,0,1,2,2,1,0,0,0,0,0,0}, {1,1,3,3,3,2,4,1,1,1,4,4,4,0,0,1,2,2,1,0,0,0,0,0,0}, {0,1,1,2,2,2,4,4,4,4,4,4,4,0,0,1,1,1,1,0,0,0,4,4,0}, {0,0,1,1,2,2,1,0,0,2,3,4,4,0,0,0,0,0,0,0,0,0,4,4,0}, {4,4,1,1,2,2,1,1,0,1,1,0,4,0,0,0,0,0,0,0,0,0,4,4,4}, {0,4,1,2,2,2,1,1,0,4,4,4,4,4,4,4,0,0,0,0,1,0,0,0,0}, {0,1,2,2,2,2,1,1,0,1,2,4,4,0,0,4,0,3,3,3,3,3,3,3,0}, {0,1,2,2,2,2,1,1,0,1,2,4,4,0,0,4,4,3,2,2,2,2,2,3,0}, {0,1,2,2,2,2,1,1,0,1,2,4,4,0,0,4,4,3,2,3,3,3,2,3,0}, {0,1,2,2,2,2,1,1,0,1,2,4,4,0,0,4,4,3,2,3,2,2,2,3,0}, {0,1,2,2,2,2,1,1,0,1,2,4,4,0,0,4,3,3,2,3,2,3,3,3,0}, {0,1,2,2,2,2,1,1,0,1,2,4,4,0,4,4,1,2,2,3,2,0,0,0,0}, {0,1,2,2,2,2,1,1,0,1,2,4,4,0,4,3,3,3,3,3,0,0,0,0,0}, {0,1,2,2,2,2,1,1,0,1,2,4,4,4,4,0,0,0,0,0,0,0,0,0,0}, {0,1,2,2,2,2,1,1,0,1,2,4,4,0,0,0,1,0,0,0,0,0,0,0,0}, {0,1,2,2,2,2,1,1,0,1,2,4,4,0,0,0,1,0,0,0,1,1,1,0,0}, {0,1,2,2,2,2,1,1,0,1,2,4,4,0,0,1,0,0,0,0,0,1,1,0,0}, {0,0,1,2,2,2,1,0,0,0,4,4,4,0,0,1,1,0,0,0,0,0,1,0,0}}; then I want to change the first couple (x and y) of integers: tempMapMatrix[100][100] = {{5,5,5,5,5,1,2,3}, {5,5,5,5,5,1,2,3}, {5,5,1,1,1,1,2,3}, {5,5,1,5,5,1,2,3}, {5,5,1,1,1,1,2,3}, {5,5,5,5,5,5,5,5}, {5,5,5,5,5,1,2,3}, {5,2,2,2,5,1,2,3}, {5,2,5,2,5,1,2,3}, {5,2,2,2,5,1,2,3}}; but I'm getting an error (Expected Expression). I've tried tempMapArray = stuff; tempMapArray[][] = stuff; but none work. Any way to change the first couple of ints in the matrix?
You need to iterate over them, this is C, you don't have syntactic sugar to assingn pieces of arrays like you want. If you want to change, for example, every first element of each row you could do something like: for (int = 0; i < 100; ++i) { tempMatrix[i][0] = 5; } so for the first couple of every row you should do for (int = 0; i < 100; ++i) { tempMatrix[i][0] = 5; tempMatrix[i][1] = 5; } and so on.
You have to access and change each element in the matrix individually. I.e.: tempMapMatrix[0][0] = 5; tempMapMatrix[0][1] = //... There is no way to "batch change" the contents of an array (one-dimensional or n-dimensional) in C. The easiest way to achieve this effect is to write a for-loop and iterate over the contents of the two dimensional array and insert the required values in the required places.
how to get the size of the following array
int first[] = {1, 4}; int second[] = {2, 3, 7}; arrayOfCPointers[0] = first; arrayOfCPointers[1] = second; NSLog(#"size of %lu", sizeof(arrayOfCPointers[0]) / sizeof(int)); I want to have an array of sub arrays. Each sub array needs to be a different size. But I need to be able to find out the size of each sub array? The Log keeps returning 1
You need to store the size somewhere. The language does not do so for bare C arrays. All you have is the address of the first element.
I'd write a wrapper class or struct to hold the array and it's metadata (like length). typedef struct tag_arrayholder { int* pArray; int iLen; }ArrayHolder; int first[] = {1, 4}; ArrayHolder holderFirst; holderFirst.pArray = first; holderFirst.iArrayLen = sizeof(first) / sizeof(int); arrayOfCPointers[0] = holderFirst; NSLog(#"size of %lu", arrayOfCPointers[0].iLen); Or, like trojanfoe said, store special value marking the last position (exactly the approach zero-terminated string uses)
The "sizeof" instruction could be used to know the amount of bytes used by the array, but it works only with static array, with dynamics one it returns the pointer size. So with static array you could use this formula : sizeof(tab)/sizeof(tab[0]) to know the size of your array because the first part give you the tab size in bytes and the second the size of an element, so the result is your amount of element in your array ! But with a dynamic array the only way is to store the size somewhere or place a "sentinal value" at the end of your array and write a loop which count elements for you ! (Sorry for my English i'm french :/)
The NSLog statement is printing the value 1 because the expression you're using is dividing the size of the first element of the array (which is the size of an int) by the size of an int. So what you currently have is this: NSLog(#"size of %lu", sizeof(arrayOfCPointers[0]) / sizeof(int)); If you remove the array brackets, you'll get the value you're looking for: NSLog(#"size of %lu", sizeof(arrayOfCPointers) / sizeof(int)); As other answers have pointed out, this won't work if you pass the array to another method or function, since all that's passed in that case is an address. The only reason the above works is because the array's definition is in the local scope, so the compiler can use the type information to compute the size.