2011-08-25 43 views
6

Có cách nào dễ dàng để tìm kiếm số NSArray để tìm gần nhất (hoặc chính xác nếu nó tồn tại) khớp với số người dùng nhập không?Tìm kiếm một NSArray cho (các) số gần nhất

Giả sử tôi có một mảng như sau: 7, 23, 4, 11, 18, 2 và người dùng nhập 5. chương trình

trả về ba giá trị gần nhất trong thứ tự của sự gần gũi tự giảm dần: 4, 7, 2, và quan trọng nhất là cung cấp cho các chỉ số NSArray trong ba đối tượng: 2, 0, 5.

+0

Tôi nghĩ cách dễ nhất là tìm kiếm inimun số, loại bỏ nó, và tìm số minimun một lần nữa, vv – TommyG

+0

Tôi bắt đầu bằng cách viết một vòng lặp cho rằng kiểm tra từng số, nhưng tôi nhanh chóng nhận ra rằng tôi sẽ không thể có được chỉ số của các đối tượng từ bản gốc mảng. Việc tìm kiếm chỉ mục đó rất quan trọng đối với chương trình thực tế của tôi - những gì tôi đăng ở đây là một ví dụ đơn giản để tôi có thể tìm hiểu lý thuyết – REDMX

+0

nó khá đơn giản để theo dõi chỉ mục ban đầu của số bạn tìm thấy, nhưng bạn có thể làm điều đó mà không cần sửa đổi mảng. Giả sử bạn tìm min, bạn giữ chỉ mục của nó (đó là số đầu tiên của bạn trong số ba), sau đó thay vì xóa nó, bạn có thể thay thế bằng số tối đa - theo cách này, bạn không thay đổi mảng của mình .... – TommyG

Trả lời

5

Cập nhật: xem dưới đây để biết một giải pháp tốt hơn so với một đầu tiên của tôi.

Đây là giải pháp sử dụng trình bao bọc NSDictionary cho mỗi số và chỉ mục của nó, với phân loại bằng khối so sánh. Nó có thể không quy mô rất tốt, nhưng nó được thực hiện công việc.

static NSString *const kValueKey = @"value"; 
static NSString *const kIndexKey = @"index"; 

+ (void)searchArray:(NSArray *)array forClosestValuesTo:(int)value resultValues:(NSArray **)values resultIndexes:(NSArray **)indexes 
{ 
    NSMutableArray *searchObjs = [NSMutableArray arrayWithCapacity:[array count]]; 

    [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 
     [searchObjs addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, kValueKey, [NSNumber numberWithUnsignedInt:idx], kIndexKey, nil]]; 
    }]; 

    [searchObjs sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { 
     NSUInteger d1 = ABS([[obj1 objectForKey:kValueKey] intValue] - value); 
     NSUInteger d2 = ABS([[obj2 objectForKey:kValueKey] intValue] - value); 
     if (d1 == d2) { return NSOrderedSame; } 
     if (d1 < d2) { return NSOrderedAscending; } 
     return NSOrderedDescending; 
    }]; 

    NSArray *results = [searchObjs subarrayWithRange:NSMakeRange(0, 3)]; 

    if (values) { 
     *values = [results valueForKey:kValueKey]; 
    } 

    if (indexes) { 
     *indexes = [results valueForKey:kIndexKey]; 
    } 
} 

Cập nhật: đây là một giải pháp cập nhật mà sắp xếp một mảng C các chỉ số, loại bỏ sự cần thiết của giấy gói NSDictionary

static NSString *const kValueKey = @"value"; 
static NSString *const kArrayKey = @"array"; 

int 
CSCompareIndexes(void *data, const void *value1, const void *value2) 
{ 
    NSDictionary *dict = (NSDictionary *)data; 

    NSArray *array = [dict objectForKey:kArrayKey]; 
    int valueToFind = [[dict objectForKey:kValueKey] intValue]; 

    int index1 = *(int *)value1; 
    int index2 = *(int *)value2; 

    NSNumber *num1 = [array objectAtIndex:index1]; 
    NSNumber *num2 = [array objectAtIndex:index2]; 

    return ABS([num1 intValue] - valueToFind) - ABS([num2 intValue] - valueToFind); 
} 

void 
CSSearchNumberArray(NSArray *array, int valueToFind, NSArray **resultValues, NSArray **resultIndexes) 
{ 
    NSInteger numValues = [array count]; 

    NSUInteger *indexes = malloc(sizeof(NSUInteger) * numValues); 
    assert(indexes); 

    int i; 
    for (i = 0; i < numValues; i++) { 
     indexes[i] = i; 
    } 

    NSDictionary *data = [NSDictionary dictionaryWithObjectsAndKeys:array, kArrayKey, [NSNumber numberWithInt:valueToFind], kValueKey, nil]; 
    qsort_r(indexes, numValues, sizeof(NSUInteger), (void *)data, CSCompareIndexes); 

    NSMutableArray *tmpValues = [NSMutableArray arrayWithCapacity:3], 
        *tmpIndexes = [NSMutableArray arrayWithCapacity:3]; 

    for (i = 0; i < 3; i++) { 
     [tmpValues addObject:[array objectAtIndex:indexes[i]]]; 
     [tmpIndexes addObject:[NSNumber numberWithInt:indexes[i]]]; 
    } 

    if (resultValues) { 
     *resultValues = [NSArray arrayWithArray:tmpValues]; 
    } 

    if (resultIndexes) { 
     *resultIndexes = [NSArray arrayWithArray:tmpIndexes]; 
    } 

    free(indexes); 
} 

int main (int argc, char *argv[]) 
{ 
    NSAutoreleasePool *pool = [NSAutoreleasePool new]; 

    NSMutableArray *test = [NSMutableArray array]; 

    int i; 
    for (i = 0; i < 10; i++) { 
     [test addObject:[NSNumber numberWithInt:(arc4random() % 100)]]; 
    } 

    NSLog(@"Searching: %@", test); 

    NSArray *values, *indexes; 
    CSSearchNumberArray(test, 50, &values, &indexes); 

    NSLog(@"Values: %@", values); 
    NSLog(@"Indexes: %@", indexes); 

    [pool drain]; 
    return 0; 
} 
+0

Điều này thực sự hoạt động hoàn hảo - Cảm ơn bạn rất nhiều! Tôi sẽ không bao giờ tự mình phát hiện ra nó – REDMX

+1

IMO, không cần từ điển.ISTM chúng đắt tiền quá mức. Nếu bạn có chỉ mục, bạn sẽ tự động có số chỉ mục, vì vậy đã có một liên kết. –

+0

@Rudy đó là sự thật. Tôi sẽ sớm cập nhật câu trả lời của tôi. –

0

Đây có phải là câu hỏi về bài tập về nhà không? Một cách dễ dàng để mã hóa điều này bằng cách sử dụng apis là tạo một mảng gọi là distances chứa khoảng cách từ mỗi số gốc, tạo một phiên bản được sắp xếp của mảng đó được gọi là sorted, sau đó tìm kiếm distances cho ba số thấp nhất trong sorted để lấy chỉ mục từ bạn có thể tra cứu số gốc.

+0

không phải bài tập về nhà, quá cũ cho rằng, Chỉ cần một câu hỏi newb. - Tôi thu thập hầu hết những gì bạn đã viết từ nghiên cứu trước đó, nhưng những gì tôi không thể tìm ra là làm thế nào để có được chỉ số từ mảng ban đầu. Tôi có cần sử dụng khóa/giá trị trong 'khoảng cách' trong đó khóa là khoảng cách và giá trị là chỉ mục gốc trong mảng không? Hoặc tôi thiếu một cái gì đó rõ ràng hơn? – REDMX

+0

@REDMX: Sẽ hữu ích khi đưa vào bản tóm tắt nghiên cứu hoặc đoạn mã của bạn, ngay cả khi chúng không hoạt động, trong phần câu hỏi của bạn, để người trả lời không kết thúc việc băm nhỏ những gì bạn đã biết và có thể cung cấp thêm thông tin phù hợp, tập trung. Đó là lý do tại sao tôi hỏi "Bạn đã thử những gì cho đến nay?" –

+0

@REDMX: xem câu trả lời đã sửa đổi của tôi. Tôi tạo một mảng các chỉ mục và sắp xếp theo khoảng cách của các con số tới giá trị tìm kiếm. Phương thức trả về một mảng với ba mục đầu tiên của mảng với các giá trị. Mảng chứa các chỉ mục ** ** của 3 giá trị gần nhất. Không cần cho một loạt các dictonaries hoặc một số như vậy, kể từ khi các chỉ số trả về (như NSNumbers) trực tiếp liên kết đến các giá trị. –

1

Phương pháp ngây thơ sẽ được tìm kiếm trên mảng nguồn cho 5, làm tăng số lần phát hiện và lưu trữ các thông tin thích hợp nếu tìm thấy, sau đó tìm kiếm 46 vv

Một phương pháp thứ hai sẽ được giữ một sắp xếp bản sao của mảng nguồn: 2, 4, 7, 11, 18, 23

sau đó sử dụng -indexOfObjectPassingTest: để tìm ra số đầu tiên lớn hơn 5 trong mảng đó, sau đó so sánh con số đó với hàng xóm trái của nó, để xem đó là gần gũi hơn với 5:

(7-5) < (5-4) ? storeinfo(7) : storeinfo(4)

Nếu người hàng xóm bên trái thắng, lưu trữ thông tin của nó, sau đó so sánh hàng xóm trái của nó với bản gốc hơn hơn lăm số:

(7-5) < (5-2) ? storeinfo(7) : storeinfo(2)

Nhưng nếu phía bên phải thắng, so sánh người hàng xóm phù hợp với người thua cuộc:

(11-5) < (5-2) ? storeinfo(11) : storeinfo(2)

Bạn chỉ cần thực hiện ba so sánh trong trường hợp này và bạn cần phải quyết định xem bạn có muốn sử dụng < hoặc <= hay không. Mảng thứ hai của bạn chỉ là kích thước n * ptr, vì vậy nó không phải là một sự tăng trưởng không gian lớn.

+0

+ 1 cho phương pháp thứ hai, bạn có thể muốn sử dụng abs() để sự khác biệt là tích cực cho dù bạn có trừ một giá trị lớn hơn hay không. Tôi cho rằng nếu bạn đang làm điều này một cách năng động, bạn sẽ không biết nên sử dụng 7-5 hay 5-7. –

+0

Lúc đầu, tôi có suy nghĩ tương tự, nhưng miễn là bạn đang hoạt động trên một mảng đã sắp xếp * và * bạn đã chắc chắn tìm thấy mục nhập đầu tiên lớn hơn số đích, bạn luôn có thể biết rằng mục bạn tìm thấy là lớn hơn '5' và mục nhập bên trái của nó nhỏ hơn hoặc bằng' 5'. – matthias

2

Sắp xếp mảng giá trị hiện tại "gián tiếp", sử dụng một loạt chỉ mục và sắp xếp theo "khoảng cách" cho giá trị tìm kiếm. Ba mục đầu tiên sau khi sắp xếp là các giá trị "gần nhất".

Ví dụ:

#import <Foundation/Foundation.h> 

@interface NearestSearcher : NSObject { } 
+ (NSArray *) searchNearestValuesOf: (int) value inArray: (NSArray *) values; 
@end 

@implementation NearestSearcher 

+ (NSArray *) searchNearestValuesOf: (int) value inArray: (NSArray *) values 
{ 
    // set up values for indexes array 
    NSMutableArray *indexes = [NSMutableArray arrayWithCapacity: values.count]; 
    for (int i = 0; i < values.count; i++) 
     [indexes addObject: [NSNumber numberWithInt: i]]; 

    // sort indexes 
    [indexes sortUsingComparator: ^NSComparisonResult(id obj1, id obj2) 
    { 
     int num1 = abs([[values objectAtIndex: [obj1 intValue]] intValue] - value); 
     int num2 = abs([[values objectAtIndex: [obj2 intValue]] intValue] - value); 

     return (num1 < num2) ? NSOrderedAscending : 
       (num1 > num2) ? NSOrderedDescending : 
           NSOrderedSame; 
    }]; 

    return [indexes subarrayWithRange: NSMakeRange(0, 3)]; 
} 
@end 


// DEMO 

#define NUM_VALUES 20 

int main (int argc, const char * argv[]) 
{ 

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 

    // DEMO SETUP 

    // set up values array with random values 
    NSMutableArray *values = [NSMutableArray arrayWithCapacity: NUM_VALUES]; 
    for (int i = 0; i < NUM_VALUES; i++) 
     [values addObject: [NSNumber numberWithInt: arc4random() % 200]]; 

    // display values array 
    for (int i = 0; i < values.count; i++) 
     NSLog(@"%2d: %4d", i, [[values objectAtIndex: i] intValue]); 

    // get a random value for x 
    int x = arc4random() % 200; 

    // METHOD INVOCATION 

    NSArray *results = [NearestSearcher searchNearestValuesOf: x inArray: values]; 

    // SHOW RESULTS 

    NSLog(@"------------------------"); 

    NSLog(@"x: %d", x); 
    for (NSNumber *num in results) 
     NSLog(@"%@: %@", num, [values objectAtIndex: [num intValue]]); 

    [pool drain]; 
    return 0; 
} 
+0

Tại sao bạn chỉ trả về dải 0-3? (NSMakeRange (0, 3)) – zakdances

+0

Xem câu hỏi. –

0

Tested Mã số: 100% các công trình

NSMutableArray *arrayWithNumbers=[[NSMutableArray alloc]initWithObjects:[NSNumber numberWithInt:7],[NSNumber numberWithInt:23],[NSNumber numberWithInt:4],[NSNumber numberWithInt:11],[NSNumber numberWithInt:18],[NSNumber numberWithInt:2],nil]; 

NSLog(@"arrayWithNumbers : %@ \n\n",arrayWithNumbers); 





NSMutableArray *ResultArray = [ [ NSMutableArray alloc] init]; 

NSMutableArray *lowestArray = [ [ NSMutableArray alloc] init]; 

NSMutableArray *tempArray = [ [ NSMutableArray alloc] init]; 

NSMutableArray *indexArray = [ [ NSMutableArray alloc] init]; 


NSNumber *numberToFind=[NSNumber numberWithInt:5]; 

int limitToFilter = 3; 


    for (NSNumber *number in arrayWithNumbers) { 

     int a=[number intValue]-[numberToFind intValue]; 

     [lowestArray addObject:[NSNumber numberWithInt:abs(a)]]; 


    } 

tempArray=[lowestArray mutableCopy]; 

NSSortDescriptor *LowestTohighest = [NSSortDescriptor sortDescriptorWithKey:@"self" ascending:YES]; 

[lowestArray sortUsingDescriptors:[NSArray arrayWithObject:LowestTohighest]]; 

int upto = limitToFilter-[ResultArray count]; 

for (int i = 0; i < upto; i++) { 


    [lowestArray objectAtIndex:i]; 

    if ([tempArray containsObject:[lowestArray objectAtIndex:i]]) { 


     NSUInteger index=[tempArray indexOfObject:[lowestArray objectAtIndex:i]]; 



     [ResultArray addObject:[arrayWithNumbers objectAtIndex:index]]; 


     [indexArray addObject:[NSIndexSet indexSetWithIndex:index]]; 



    } 

} 

NSLog(@"ResultArray is : %@ \n\n",ResultArray); 

NSLog(@"indexArray is : %@ \n\n",indexArray); 


    //here release all 4 arrays if u dont need them 

OUTPUT:

arrayWithNumbers: ( 7, 23, 4, 11, 18,)

ResultArray là: ( 4, 7,)

indexArray là: (

"<NSIndexSet: 0x4e06620>[number of indexes: 1 (in 1 ranges), indexes: (2)]", 

    "<NSIndexSet: 0x4e04030>[number of indexes: 1 (in 1 ranges), indexes: (0)]", 

    "<NSIndexSet: 0x4e06280>[number of indexes: 1 (in 1 ranges), indexes: (5)]" 

     ) 
Các vấn đề liên quan