【问题标题】:Searching a NSArray for the nearest number(s)在 NSArray 中搜索最近的数字
【发布时间】:2011-08-27 00:19:09
【问题描述】:

是否有一种简单的方法可以搜索NSArray 的数字以找到与用户输入数字最接近(或精确,如果存在)的匹配项?

假设我有一个这样的数组:7, 23, 4, 11, 18, 2,用户输入5。

程序以接近程度的降序返回三个最接近的值:4, 7, 2,最重要的是给出三个对象的NSArray索引:2, 0, 5。

【问题讨论】:

  • 我认为最简单的方法是查找最小值,将其删除,然后再次查找最小值,等等。
  • 我首先编写了一个 for 循环来测试每个数字,但我很快意识到我无法从原始数组中获取对象的索引。找到该索引对于我的实际程序非常重要 - 我在这里发布的是一个简单的示例,因此我可以学习理论
  • 追踪你找到的数字的原始索引应该很简单,但你可以在不修改数组的情况下做到这一点。假设你找到了最小值,你保留它的索引(这是你三个中的第一个数字),然后你可以用最大数字替换它而不是删除它 - 这样,你就不会改变你的数组....跨度>

标签: objective-c search nsarray


【解决方案1】:

更新:请参阅下文以获得比我的第一个更好的解决方案。

这是一个对每个数字及其索引使用 NSDictionary 包装器的解决方案,并使用比较器块进行排序。它可能无法很好地扩展,但可以完成工作。

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 索引数组进行排序的更新解决方案,无需 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;
}

【讨论】:

  • 这实际上非常有效 - 非常感谢!我自己永远也想不通
  • IMO,不需要字典。 ISTM 他们是昂贵的矫枉过正。如果你有索引,你会自动拥有它索引的数字,所以已经有一个关联。
【解决方案2】:

使用索引数组“间接”对现有值数组进行排序,并按与搜索值的“距离”排序。排序后的前三个项目是“最近”的值。

例子:

#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-3的范围? ( NSMakeRange(0, 3) )
【解决方案3】:

简单的方法是在源数组中搜索5,增加找到的计数并存储相应的信息(如果找到),然后搜索4 和6,等等。

第二种方法是保留源数组的排序副本: 2, 4, 7, 11, 18, 23

然后使用-indexOfObjectPassingTest:在该数组中找到第一个大于5的数字,然后将该数字与其左邻比较,看看哪个更接近5:

(7-5) &lt; (5-4) ? storeinfo(7) : storeinfo(4)

如果左邻获胜,则存储其信息,然后将 其左邻与原始大于 5 的数字进行比较:

(7-5) &lt; (5-2) ? storeinfo(7) : storeinfo(2)

但如果右侧获胜,则将其右侧邻居与失败者进行比较:

(11-5) &lt; (5-2) ? storeinfo(11) : storeinfo(2)

在这种情况下,您应该只需要进行三个比较,并且您需要决定是要使用&lt; 还是&lt;=。你的第二个数组只有 n*ptr 大小,所以它不是一个巨大的空间增长。

【讨论】:

  • + 1 对于第二种方法,您可能希望使用 abs() 以便无论您减去更大的值,差异都是正数。我假设如果您动态执行此操作,您将不知道是使用 7-5 还是 5-7。
  • 一开始我也有同样的想法,但是只要你在一个有序数组上操作并且你确保找到大于目标的第一个条目号,你总能知道你找到的那个条目大于5,它左边的那个条目小于等于5。
【解决方案4】:

这是一道作业题吗?使用 api 进行编码的一种简单方法是生成一个名为 distances 的数组,其中包含与每个原始数字的距离,创建该数组的一个名为 sorted 的排序版本,然后搜索 distances 中的最低三个数字sorted 获取索引,您可以从中查找原始号码。

【讨论】:

  • 不是作业,太老了,只是一个新手问题。 -- 我收集了你以前研究中写的大部分内容,但我不知道如何从原始数组中获取索引。我是否需要在distances 中使用键/值,其中键是距离,值是数组中的原始索引?还是我遗漏了一些更明显的东西?
  • @REDMX:在您的问题正文中包含您的研究或代码 sn-ps 的摘要(即使它们不起作用)很有帮助,这样回答者就不会结束重新散列您已经知道的内容,并可以提供更相关、更集中的信息。这就是为什么我问“到目前为止你尝试过什么?”
  • @REDMX:查看我修改后的答案。我创建了一个索引数组,并按数字与搜索值的距离对其进行排序。该方法返回一个数组,其中包含数组的前三项和值。该数组包含 3 个最接近值的 索引。不需要字典数组或类似的数组,因为返回的索引(作为 NSNumbers)直接链接到值。
  • 你通过搜索“距离”得到索引,索引将与原始数组中的相同。
【解决方案5】:

测试代码:100% 有效

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

输出:

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

结果数组是:( 4、 7、 2 )

indexArray 是:(

    "<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)]"

        ) 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-22
    • 2012-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-28
    相关资源
    最近更新 更多