Comparing Multiple Word names with Levenshtein Distances - objective-c

I'm comparing building names on my campus with input from various databases. People entered these names, and everyone uses their own abbreviation scheme. I'm trying to find the best match from a user input to a canonical form of the name.
I've implemented a recursive Levenshtein Distance method, but there are a few edge cases I'm trying to tackle. My implementation is on GitHub.
Some of the building names are one word, while others are two. A single word on a single word produces fairly accurate results, but there are two things that I need to keep in mind.
Abbreviations: Assuming an input is a shortened version of a name, I can sometimes get the same Levenshtein Distance between the input and an arbitrary name, as well as the correct name.
For example, if my input is "Ing" and the building names1. are ["Boylan", "Ingersoll", "Whitman", "Whitehead", "Roosevelt", and "Library"], I end up with a LD of 6 for both Boylan and Ingersoll. The desired result is here Ingersoll.
Multiword Strings: The second edge cases is when the input and/or output is two words, separated by a space. For example, New Ing is an abbreviation for New Ingersoll. In this case, New Ingersoll and Boylan both score a Levenshtein Distance of 6. If I were to split the strings, New matches New perfectly, and then I just have to refer back to the solution to my previous edge case.
What's the best way to handle these two edge cases?
1. For the curious, these are the buildings at Brooklyn College, in New York City.

I think you should use the length of the Longest Common Subsequence instead of the Levenshtein Distance. That seems to be a better metric for your case. In essence, it prioritizes insertions and deletions over substitutions, as I suggested in my comment.
It clearly distiguishes between "Ing" -> "Ingersoll" and "Ing" -> "Boylan" (scores of 3 and 1) handles spaces without a problem ("New Ing" -> "New Ingersoll" scores 7 where "New Ing" -> "Boylan" again scores 1), and will also work nicely should you come across an abbreviation like "Ingsl".
The algorithm is straightforward. Where your two strings have length m and n, compare successive prefixes of the strings characterwise (starting with the empty prefixes), keeping scores in a matrix of size m+1, n+1. If a particular pair matches, add one to the score of the previous two prefixes (one row up and one column left in the matrix); otherwise keep the highest of the two scores of those prefixes (compare the entry immediately above and the entry immediately left and take the best). When you've gone through both strings, the last entry in the score matrix is the length of the LCS.
Example score matrix for "Ingsll" and "Ingersoll":
0 1 2 3 4 5 6
I n g s l l
---------------
0 | 0 0 0 0 0 0 0
1 I | 0 1 1 1 1 1 1
2 n | 0 1 2 2 2 2 2
3 g | 0 1 2 3 3 3 3
4 e | 0 1 2 3 3 3 3
5 r | 0 1 2 3 3 3 3
6 s | 0 1 2 3 4 4 4
7 o | 0 1 2 3 4 4 4
8 l | 0 1 2 3 4 5 5
9 l | 0 1 2 3 4 5 6
Here's an ObjC implementation of the length. Most of the complexity here is just due to wanting to handle composed character sequences -- #"o̶" for example -- correctly.
#import <Foundation/Foundation.h>
#interface NSString (WSSComposedLength)
- (NSUInteger)WSSComposedLength;
#end
#implementation NSString (WSSComposedLength)
- (NSUInteger)WSSComposedLength
{
__block NSUInteger length = 0;
[self enumerateSubstringsInRange:(NSRange){0, [self length]}
options:NSStringEnumerationByComposedCharacterSequences | NSStringEnumerationSubstringNotRequired
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
length++;
}];
return length;
}
#end
#interface NSString (WSSLongestCommonSubsequence)
- (NSUInteger)WSSLengthOfLongestCommonSubsequenceWithString:(NSString *)target;
- (NSString *)WSSLongestCommonSubsequenceWithString:(NSString *)target;
#end
#implementation NSString (WSSLongestCommonSubsequence)
- (NSUInteger)WSSLengthOfLongestCommonSubsequenceWithString:(NSString *)target
{
NSUInteger * const * scores;
scores = [[self scoreMatrixForLongestCommonSubsequenceWithString:target] bytes];
return scores[[target WSSComposedLength]][[self WSSComposedLength]];
}
- (NSString *)WSSLongestCommonSubsequenceWithString:(NSString *)target
{
NSUInteger * const * scores;
scores = [[self scoreMatrixForLongestCommonSubsequenceWithString:target] bytes];
//FIXME: Implement this.
return nil;
}
- (NSData *)scoreMatrixForLongestCommonSubsequenceWithString:(NSString *)target{
NSUInteger selfLength = [self WSSComposedLength];
NSUInteger targetLength = [target WSSComposedLength];
NSMutableData * scoresData = [NSMutableData dataWithLength:(targetLength + 1) * sizeof(NSUInteger *)];
NSUInteger ** scores = [scoresData mutableBytes];
for( NSUInteger i = 0; i <= targetLength; i++ ){
scores[i] = [[NSMutableData dataWithLength:(selfLength + 1) * sizeof(NSUInteger)] mutableBytes];
}
/* Ranges in the enumeration Block are the same measure as
* -[NSString length] -- i.e., 16-bit code units -- as opposed to
* _composed_ length, which counts code points. Thus:
*
* Enumeration will miss the last character if composed length is used
* as the range and there's a substring range with length greater than one.
*/
NSRange selfFullRange = (NSRange){0, [self length]};
NSRange targetFullRange = (NSRange){0, [target length]};
/* Have to keep track of these indexes by hand, rather than using the
* Block's substringRange.location because, e.g., #"o̶", will have
* range {x, 2}, and the next substring will be {x+2, l}.
*/
__block NSUInteger col = 0;
__block NSUInteger row = 0;
[target enumerateSubstringsInRange:targetFullRange
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString * targetSubstring,
NSRange targetSubstringRange,
NSRange _, BOOL * _0)
{
row++;
col = 0;
[self enumerateSubstringsInRange:selfFullRange
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString * selfSubstring,
NSRange selfSubstringRange,
NSRange _, BOOL * _0)
{
col++;
NSUInteger newScore;
if( [selfSubstring isEqualToString:targetSubstring] ){
newScore = 1 + scores[row - 1][col - 1];
}
else {
NSUInteger upperScore = scores[row - 1][col];
NSUInteger leftScore = scores[row][col - 1];
newScore = MAX(upperScore, leftScore);
}
scores[row][col] = newScore;
}];
}];
return scoresData;
}
#end
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSArray * testItems = #[#{#"source" : #"Ingso̶ll",
#"targets": #[
#{#"string" : #"Ingersoll",
#"score" : #6,
#"sequence" : #"Ingsll"},
#{#"string" : #"Boylan",
#"score" : #1,
#"sequence" : #"n"},
#{#"string" : #"New Ingersoll",
#"score" : #6,
#"sequence" : #"Ingsll"}]},
#{#"source" : #"Ing",
#"targets": #[
#{#"string" : #"Ingersoll",
#"score" : #3,
#"sequence" : #"Ing"},
#{#"string" : #"Boylan",
#"score" : #1,
#"sequence" : #"n"},
#{#"string" : #"New Ingersoll",
#"score" : #3,
#"sequence" : #"Ing"}]},
#{#"source" : #"New Ing",
#"targets": #[
#{#"string" : #"Ingersoll",
#"score" : #3,
#"sequence" : #"Ing"},
#{#"string" : #"Boylan",
#"score" : #1,
#"sequence" : #"n"},
#{#"string" : #"New Ingersoll",
#"score" : #7,
#"sequence" : #"New Ing"}]}];
for( NSDictionary * item in testItems ){
NSString * source = item[#"source"];
for( NSDictionary * target in item[#"targets"] ){
NSString * targetString = target[#"string"];
NSCAssert([target[#"score"] integerValue] ==
[source WSSLengthOfLongestCommonSubsequenceWithString:targetString],
#"");
// NSCAssert([target[#"sequence"] isEqualToString:
// [source longestCommonSubsequenceWithString:targetString]],
// #"");
}
}
}
return 0;
}

I think the Levenshtein distance is only useful when you are dealing with nearly similar words like casual misspellings. If the Levenshtein distance is longer than the word itself, it has no valuable meaning as likeness value. (In your example, "Ing" and "Boylan" haven't got anything in common; no-one would confuse these words. To get from "Ing" to "Boylan", you need six edits, twice as many as the word has letters.) I wouldn't even consider the Levenshtein distance between words that have significantly different lengths like "Ing" and "Ingersoll" and declare them different.
Instead, I'd check words that are shorter than the original in abbreviation mode. To check whether a word is an abbreviation of a longer word, you could check that all letters of the abbreviation appear in the original in the same order. You should also enforce that the words start with the same letter. That method doesn't account for mistyped abbreviations, however.
I think that multiword strings are better parsed word-wise. Do you need to distinguish between Ingersoll and New Ingersoll? In that case, you could establish a scoring system where a word match scores 100, maybe with ten times the Levenshtein distance subtracted. A non-match has a negative score, say -100. Then you assess the score of each word and divide by the number of words in the building:
If your string is "Ingersoll":
"Ingersoll" scores 100 / 1 == 100
"New Ingersoll" scores 100 / 2 == 50
If your string is "New Ingersoll":
"Ingersoll" scores (100 - 100) / 1 == 100
"New Ingersoll" scores (100 + 100) / 2 == 100
The word-wise approach falls flat when you have abbreviations that contain letters from various words, e.g. "NI" or "NIng" for New Ingersoll, so maybe you should try the abbreviation match above on the whole building name if you can't find an match in word-to-word matching.
(I realise that all this isn't really an answer, but more a loose bunch of thoughts.)

Related

Random Generator slightly less random

NSPredicate *predicate = [NSPredicate predicateWithFormat:#"category == %#", selectedCategory];
NSArray *filteredArray = [self.Quotes filteredArrayUsingPredicate:predicate];
// Get total number in filtered array
int array_tot = (int)[filteredArray count];
// As a safeguard only get quote when the array has rows in it
if (array_tot > 0) {
// Get random index
int index = (arc4random() % array_tot);
// Get the quote string for the index
NSString *quote = [[filteredArray objectAtIndex:index] valueForKey:#"quote"];
// Display quote
self.quote_text.text = quote;
// Update row to indicate that it has been displayed
int quote_array_tot = (int)[self.Quotes count];
NSString *quote1 = [[filteredArray objectAtIndex:index] valueForKey:#"quote"];
for (int x=0; x < quote_array_tot; x++) {
NSString *quote2 = [[Quotes objectAtIndex:x] valueForKey:#"quote"];
if ([quote1 isEqualToString:quote2]) {
NSMutableDictionary *itemAtIndex = (NSMutableDictionary *)[Quotes objectAtIndex:x];
[itemAtIndex setValue:#"DONE" forKey:#"source"];
}
}
Above is the code I use in my app for generating a random quote from one of two categories stored in a plist (in arrays, where the first line is category, and second is quote). However, it seems to have a preference of repeating ones it's already shown. I'd prefer it have a preference (but not exclusively) show ones it hasn't shown before.
Your question is an algorithm question. What you want is a sequence of numbers that seems random but is more uniform.
What you are looking for is called a low-discrepancy sequence. A simple form of this is a "shuffle bag", often used in game development, as described here or here.
With a shuffle bag, you basically generate all the indices (e.g. 0 1 2 3 4 5), shuffle them (e.g. 2 3 5 1 0 4) and then display the elements in this order. At the end, you generate another sequence (e.g. 4 1 0 2 3 5). Note that it is possible that the same element appears twice in the sequence, although it is rare. E.g. in this case, the "4" is a duplicate, because the full sequence is 2 3 5 1 0 4 4 1 0 2 3 5.
arc4random() is a good PRNG on Apple platforms, so it doesn't give you a "low discrepancy sequence". But: you can use it as a primitive to generate "low discrepancy sequences", you can also use it as a primitive to create a shuffle bag implementation.

Get sequence of random numbers' pairs (Objective-c)

Good morning, i'm trying to generate a sequence of N pairs of numbers, for example 1-0, 2-4, 4-3. These numbers must range between 0 and 8 and the pair must be different for all the numbers.
I don't want that: 1-3 1-3
I found that if a and b are the numbers, (a+b)+(a-b) has to be different for all couples of numbers.
So I manage to do that, but the loop never ends.
Would you please correct my code or write me another one? I need it as soon as possible.
NSNumber*number1;
int risultato;
int riga;
int colonna;
NSMutableArray*array=[NSMutableArray array];
NSMutableArray*righe=[NSMutableArray array];
NSMutableArray*colonne=[NSMutableArray array];
for(int i=0; i<27; i++)
{
riga=arc4random()%9;
colonna=arc4random()%9;
risultato=(riga+colonna)+(riga-colonna);
number1=[NSNumber numberWithInt:risultato];
while([array containsObject:number1])
{
riga=arc4random()%9;
colonna=arc4random()%9;
risultato=(riga+colonna)+(riga-colonna);
number1=[NSNumber numberWithInt:risultato];
}
NSNumber*row=[NSNumber numberWithBool:riga];
NSNumber*column=[NSNumber numberWithInt:colonna];
[righe addObject:row];
[colonne addObject:column];
[array addObject:number1];
}
for(int i=0; i<27; i++)
{
NSNumber*one=[righe objectAtIndex:i];
NSNumber*two=[colonne objectAtIndex:i];
NSLog(#"VALUE1 %ld VALUE2 %ld", [one integerValue], (long)[two integerValue]);
}
edit:
I have two arrays (righe, colonne) and I want them to have 27 elements [0-8].
I want to obtain a sequence like it:
righe: 1 2 4 6 7 8 2 3 4 8 8 7
colonne: 1 3 4 4 2 1 5 2 7 6 5 6
I don't want to have that:
righe: 1 2 4 6 2
colonne: 1 3 5 2 3
Where you see that 2-3 is repeated once. Then I'd like to store these values in a primitive 2d array (array[2][27])
I found that if a and b are the numbers, (a+b)+(a-b) has to be different for all couples of numbers.
This is just 2 * a and is not a valid test.
What you are looking for are pairs of digits between 0 - 8, giving a total of 81 possible combinations.
Consider: Numbers written in base 9 (as opposed to the common bases of 2, 10 or 16) use the digits 0 - 8, and if you express the decimal numbers 0 -> 80 in base 9 you will get 0 -> 88 going through all the combinations of 0 - 8 for each digit.
Given that you can can restate your problem as requiring to generate 27 numbers in the range 0 - 80 decimal, no duplicates, and expressing the resultant numbers in base 9. You can extract the "digits" of your number using integer division (/ 9) and modulus (% 9)
To perform the duplicate test you can simply use an array of 81 boolean values: false - number not used, true - number used. For collisions you can just seek through the array (wrapping around) till you find an unused number.
Then I'd like to store these values in a primitive 2d array (array[2][27])
If that is the case just store the numbers directly into such an array, using NSMutableArray is pointless.
So after that long explanation, the really short code:
int pairs[2][27];
bool used[81]; // for the collision testing
// set used to all false
memset(used, false, sizeof(used));
for(int ix = 0; ix < 27; ix++)
{
// get a random number
int candidate = arc4random_uniform(81);
// make sure we haven't used this one yet
while(used[candidate]) candidate = (candidate + 1) % 81;
// record
pairs[0][ix] = candidate / 9;
pairs[1][ix] = candidate % 9;
// mark as used
used[candidate] = true;
}
HTH
Your assumption about (a+b)+(a-b) is incorrect: this formula effectively equals 2*a, which is obviously not what you want. I suggest storing the numbers in a CGPoint struct and checking in a do...while loop if you already have the newly generated tuple in your array:
// this array will contain objects of type NSValue,
// since you can not store CGPoint structs in NSMutableArray directly
NSMutableArray* array = [NSMutableArray array];
for(int i=0; i<27; i++) {
// declare a new CGPoint struct
CGPoint newPoint;
do {
// generate values for the CGPoint x and y fields
newPoint = CGPointMake(arc4random_uniform(9), arc4random_uniform(9));
} while([array indexOfObjectPassingTest:^BOOL(NSValue* _Nonnull pointValue, NSUInteger idx, BOOL * _Nonnull stop) {
// here we retrieve CGPoint structs from the array one by one
CGPoint point = [pointValue CGPointValue];
// and check if one of them equals to our new point
return CGPointEqualToPoint(point, newPoint);
}] != NSNotFound);
// previous while loop would regenerate CGPoint structs until
// we have no match in the array, so now we are sure that
// newPoint has unique values, and we can store it in the array
[array addObject:[NSValue valueWithCGPoint:newPoint]];
}
for(int i=0; i<27; i++)
{
NSValue* value = array[i];
// array contains NSValue objects, so we must convert them
// back to CGPoint structs
CGPoint point = [value CGPointValue];
NSInteger one = point.x;
NSInteger two = point.y;
NSLog(#"VALUE1 %ld VALUE2 %ld", one, two);
}

Reverse a double value

I'm trying to reverse a double value like this:
Input: 1020304050...... Output: 5040302010
the group of 2 digits remain in the same order. So 10 doesn't become 01. or 53 doesn't become 35.
The input will always have even number of digits, so pairing isn't an issue.
The 2 adjacent digits are actually a code number for a function. And I want to maintain that so I can apply that function again.
double temp = 0.0;
double Singlefilter=1;
double reverseString=0;
temp=filtersequence;
while (temp>0)
{
Singlefilter=fmod(temp, 100.00);
temp=temp/100;
reverseString=(reverseString+Singlefilter)*100;
NSLog(#"reversed string of filter %f",reverseString);
}
But I have no idea why this isn't working. This is generating randomly very very big values.
[This question has been replaced by Reverse a double value while maintaining 2 adjacent digits in same format]
You can do it like this:
#import <Foundation/Foundation.h>
#include "math.h"
void outputGroupReversed(double filtersequence)
{
double reverseString = 0.0;
double temp = filtersequence;
while (temp > 0.0)
{
double groupMultiplier = 100.0;
double singleFilter = fmod(temp, groupMultiplier);
temp = floor(temp / groupMultiplier);
reverseString = reverseString * groupMultiplier + singleFilter;
}
NSLog(#"reversed string of filter %f", reverseString);
}
int main(int argc, const char * argv[])
{
#autoreleasepool {
outputGroupReversed(1020304050.0);
}
return 0;
}
This code does not handle input with a fractional part correctly, though.
You're better off just converting it to a string and reversing it.
NSString *inputString = [#(input) stringValue]; // Eg 1230
NSMutableString *reversedString = [NSMutableString string];
NSInteger charIndex = [inputString length];
while (charIndex > 0) {
charIndex--;
NSRange subStrRange = NSMakeRange(charIndex, 1);
[reversedString appendString:[inputString substringWithRange:subStrRange]];
}
double result = [reversedString doubleValue]; // Eg 0321 -> 321
// Go back to NSString to get the missing length
NSString *resultString = [#(result) stringValue]; // Eg. 321
// Multiple by factors of 10 to add zeros
result *= exp(10, [inputString length] - [resultString length]); // 3210
NSLog(#"reversed value %f", result);
Reverse string method from this answer.
If you wish to store 2-digit decimal integers packed together as a single numeric value you would be better of using uint64_t - unsigned long 64 bit integers rather than double. That will store 9 pairs of two decimal digits precisely which appears to be one more than you need (you mention 16 digits in a comment).
As long as the numeric value of the packed pairs is not important, just that you can pack 8 2-digit decimal numbers (i.e. 0 -> 99) into a single numeric value, then you can do better. A 64-bit integer is 8 pairs of 2-hexadecimal digit numbers, or 8 8-bit bytes, so you can store 8 values 0 -> 99 one per byte. Now adding and extracting values becomes bit-shifts (>> & << operators) by 8 and bitwise-or (|) and bitwise-and (&). This at least makes it clear you are packing values in, which divide & remainder do not.
But there is another payoff, your "reverse" operation now becomes a single call to CFSwapInt64() which reverses the order of the bytes.
However having said the above, you really should look at your model and consider another data type for what you are doing - long gone are the days when programs had to pack multiple values into words to save space.
For example, why not just use a (C) array of 8-bit (uint8_t values[8]) integers? If you require you can place that in a struct and pass it around as a single value.

Sudoku generation in Objective-c

I am supposed to make an app that generates fully complete Sudoku puzzles. I can get the first row to generate properly with the following code, but, no matter what I do, I cannot get the next rows to work right. They all generate the right way in that I get the numbers 1-9 in a random order, but I cannot get it to make a workable Sudoku puzzle.
Code:
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:8];
for (int integerA = 0; integerA < 10; integerA ++) {
[array addObject:[NSNumber numberWithInt:integerA]];
//NSLog(#"%i", (integerA + 1));
}
for (int x = 8; x >= 0; x --) {
[array exchangeObjectAtIndex:(arc4random() % (x + 1)) withObjectAtIndex:x];
section[x][0] = ([[array objectAtIndex:x] intValue] + 1);
for (int y = 8; y >= 0; y --) {
if (0) {
}
}
}
That part works. If I try to make another array and generate the "y" values for each "x" value, it goes all weird. Without numerous conditional statements, is there an efficient way to generate a fully solved Sudoku puzzle?
The "weird results":
2 0 1 | 2 3 3 | 5 2 2
7 0 3 | 2 1 5 | 1 4 4
2 0 1 | 0 3 4 | 6 7 3
- - - - - - - - - -
8 4 0 | 1 3 6 | 5 2 7
5 0 4 | 3 2 1 | 1 1 1
7 0 0 | 3 1 4 | 6 5 1
- - - - - - - - - -
2 7 0 | 5 6 3 | 4 1 8
7 6 1 | 1 3 2 | 5 0 5
0 1 1 | 2 3 5 | 0 1 5
The problem of find a correct Sudoku scheme is a very common homework and it has many different solutions.
I will not provide you the code, but let me give you some suggestions: as you certainly know, the aim is to generate a scheme that has non-repeating numbers in row, column and square.
This is, more basically, the problem of generating random non-repeating numbers.
The web is full of solutions for this problem, just a couple of examples here and here.
Now that we are able to generate non-repeating numbers, the problem is to fill the table.
There are many possible approaches: obviously you cannot avoid to check (in some way) that the current number appears only once per row, column, square.
The first solution that comes in my mind is a recursive procedure (let's call it int fill(int row)):
In a do-while loop (I'll explain later why) do this operations:
generate an array of random (this is to avoid that the algorithm generates always the same scheme) non-repeating numbers (from 1 to 9)
this is a possible candidate to become the row-esim row of your scheme. Of course, most of the case you will not be such lucky to have your row ready, so you have to check some conditions:
for each number in your array check if it's the first time it appears in column and in the square (you do not need to check in the row since you built an array of non-repeating numbers).
I will not explain further how to check since it's almost trivial.
If yes, copy it in your scheme and move it at the end of your array. This way, at the beginning of your array, you will always have non-used numbers.
If no, skip it and try the next one.
Be careful: you must keep the reference of the number of used numbers, otherwise you may use two times the same number in one row.
However, this way it is possible to arrive in a situation in which no number seems to fit your table. In this case, simply return 0
If, instead, you inserted all 9 numbers in the current row, call int chk = fill(row+1). Of course you must not call the recursive step if you reached the final row (row==9). In this case simply return 1.
chk will store the return value of the recursive step.
If chk==0 it means that the algorithm was not able to find a suitable row for the next level. In this case simply restart the initial do-while loop.
Otherwise the recursive step was successful, so you can return 1.
However, this is only a possible solutions. There are many others possible algorithms/variations. It's up to you to increase performance and do some optimization.
For sudoku generates we need to check only three - four step
Get row filled numbers array => rawArray.
Get Column filled numbers array => columnArray.
Get current cell's 3x3 grid array => cellArray.
Create array of 1-9 number (numberArray) and Remove numbers of above three array (rawArray , columnArray , cellArray)
finalArray = (numberArray) - (rawArray , columnArray , cellArray)
get random number from finalArray
and placed it to current cell
if any conflicted repeat step 1-5
bellow is code how to generate random sudoku
#import "ViewController.h"
#interface ViewController (){
NSMutableArray *field;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self genrateSudoku];
}
-(void)genrateSudoku{
[self fillEmptyGrid];
#autoreleasepool {
int n = 3;
BOOL flag=NO;
for (int i = 0; i < n*n; i++) {
for (int j = 0; j < n*n; j++){
if ([field[i][j] isEqualToString:#"_"]) {
if (![self fileValue:i and:j]) {
flag=YES;
break;
}
}else{
continue;
}
}
if (flag) {
break;
}
}
if (flag) {
[self genrateSudoku];
}else{
NSLog(#"field fill =%#",field);
}
}
}
-(void)fillEmptyGrid{
int n = 3;
field=[[NSMutableArray alloc]init];
for (int i = 0; i < n*n; i++) {
NSMutableArray *a=[[NSMutableArray alloc]init];
[field addObject:a];
for (int j = 0; j < n*n; j++){
[field[i] addObject:[NSString stringWithFormat:#"_"]];
}
}
}
-(BOOL)fileValue:(int)i and:(int)j{
NSMutableArray *rawArray=field[i];
NSMutableArray *cellArray=[self boxArray:i and:j];
NSMutableArray *columnArray=[self colArray:i and:j];
NSString *value =[self getRandomCol:columnArray rowA:rawArray box:cellArray];
if (value==nil) {
return NO;
}else{
field[i][j]=value;
return YES;
}
}
-(NSMutableArray *)boxArray:(int)i and:(int)j {
int x= (i<3)?0:((i<6)?3:6);
int y=(j<3)?0:((j<6)?3:6);
NSMutableArray *ar=[[NSMutableArray alloc]init];
for (int a=x; a<x+3; a++) {
for (int b=y; b<y+3; b++) {
[ar addObject:field[a][b]];
}
}
return ar;
}
-(NSMutableArray *)colArray:(int)i and:(int)j{
NSMutableArray *ar=[[NSMutableArray alloc]init];
for (int b=0; b<9; b++) {
[ar addObject:field[b][j]];
}
return ar;
}
-(NSString *)getRandomCol:(NSMutableArray *)col rowA:(NSMutableArray *)row box:(NSMutableArray *)box{
NSMutableArray *array=[[NSMutableArray alloc]initWithObjects:#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9", nil];
[array removeObjectsInArray:row];
[array removeObjectsInArray:box];
[array removeObjectsInArray:col];
if (array.count>0) {
int x=arc4random()%array.count;
return array[x];
}
else{
return nil;
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end

Need to sort 3 arrays by one key array

I am trying to get 3 arrays sorted by one key array in objective c for the iphone, here is a example to help out...
Array 1 Array 2 Array 3 Array 4
1 15 21 7
3 12 8 9
6 7 8 0
2 3 4 8
When sorted i want this to look like
Array 1 Array 2 Array 3 Array 4
1 15 21 7
2 3 4 8
3 12 8 9
6 7 8 0
So array 2,3,4 are moving with Array 1 when sorted.
Currently i am using a bubble sort to do this but it lags so bad that it crashes by app.
The code i am using to do this is
int flag = 0;
int i = 0;
int temp = 0;
do
{
flag=1;
for(i = 0; i < distancenumber; i++)
{
if(distance[i] > distance[i+1])
{
temp = distance[i];
distance[i]=distance[i + 1];
distance[i + 1]=temp;
temp = FlowerarrayNumber[i];
FlowerarrayNumber[i] = FlowerarrayNumber[i+1];
FlowerarrayNumber[i + 1] = temp;
temp = BeearrayNumber[i];
BeearrayNumber[i] = BeearrayNumber[i + 1];
BeearrayNumber[i + 1] = temp;
flag=0;
}
}
}while (flag==0);
where distance number is the amount of elements in all of the arrays, distance is array 1 or my key array.
and the other 2 are getting sorted.
If anyone can help me get a merge sort(or something faster, it is running on a iPhone so it needs to be quick and light) to do this that would be great i cannot figure out how the recursion works in this method and so having a hard time to get the code to work.
Any help would be greatly appreciated
Can't you simply structure your array to have A array that each item holds a array ?
Then simply sort your array based on the first item of the array it holds, or have a simple struct that holds an item and also the array.
I'm just thinking out loud here, but if all of your arrays correspond with each other (that is, BeearrayNumber[x] corresponds with FlowerarrayNumber[x], which corresponds with distance[x]), then you could consider using an array of structures rather than independent arrays. For example:
typedef struct
{
int flowerNumber;
int beeNumber;
float distance;
} BeeFlowerData;
#define MAX_BEE_FLOWER_DATA (100)
BeeFlowerData allBeeFlowers[MAX_BEE_FLOWER_DATA];
Then, you can sort using POSIX qsort:
int BeeFlowerComparator(const void *l, const void *r)
{
const BeeFlowerData *left = l;
const BeeFlowerData *right = r;
if (left->distance > right->distance)
return 1;
else if (left->distance < right->distance)
return -1;
else
return 0;
}
// somewhere in your class:
- (void) sort
{
qsort (allBeeFlowers, MAX_BEE_FLOWER_DATA, sizeof(BeeFlowerData), BeeFlowerComparator);
}
I can't believe no one has suggested wrapping them in an object yet. It's fairly trivial:
//MyObject.h
#interface MyObject : NSObject {
int a;
int b;
int c;
int d;
}
#property int a;
#property int b;
#property int c;
#property int d;
#end
//MyObject.m
#implementation MyObject
#synthesize a, b, c, d;
#end
//Elsewhere:
MyObject * m = [[MyObject alloc] init];
[m setA:1];
[m setB:15];
[m setC:21];
[m setD:7];
[myMutableArray addObject:m];
[m release];
//... do that for the rest of the sets of numbers
NSSortDescriptor * sortByA = [NSSortDescriptor sortDescriptorWithKey:#"a" ascending:YES];
[myMutableArray sortUsingDescriptors:[NSArray arrayWithObject:sortByA]];
When you do that, you'll have one array, but the objects in that array will be sorted by their "a" value in ascending order.
This is not a objective c specific question. This is an algorithmic question.
First sort the first array.
Loop through the first array and find the index for each number.
Then retrieve the value in second array corresponding to the index in step 2.
construct a new array that holds the results in step 3.
Repeat step 2,3,4 for the other arrays as well.