views:

89

answers:

2

I've only recently jumped into the world of iphone development and objective-c, so I'm still a bit lost as to how I might implement something like this.

I have a float, and I have a list of ranges that float can fall within and the corresponding value I should return, eg:

10.0 - 14.5 : 1.0
14.5 - 17.0 : 2.0
17.0 - 23.0 : 2.5
23.0 - 32.4 : 4.0

So if my float is, say, 15.12, I want to return 2.0.

The part that makes this tricky is that the range list is quite long, and I have several such range lists that I need to choose from, any of which might need to be changed later. Simply writing a few hundred or thousand if statements seems like an ugly solution to say the least.

+2  A: 

If the ranges are all adjacent, you could make a custom collection class for this, and implement it with two parallel arrays of numbers.

Each number in the first array is one end of one or two ranges:

  • 10.0
  • 14.5
  • 17.0
  • 23.0
  • 32.4

The second array holds one fewer number; these numbers are the values to map to:

  • 1.0
  • 2.0
  • 2.5
  • 4.0

Your implementation of searching this collection would consist of comparing a requested key number to each number in the keys array and the number after it. It'd be simplest to implement a linear search, at least at first (you can optimize it later with Instruments's or Shark's guidance). For example, say this object is asked about 20:

  • 10.0 ≤ 20 < 14.5? No.
  • 14.5 ≤ 20 < 17.0? No.
  • 17.0 ≤ 20 < 23.0? Yes.

Having found that the requested key value is within the third range, you return the third value (2.5).

Peter Hosey
No reason you couldn't. That's an implementation detail; the questioner can implement a linear search to get it working (at least long enough to write unit tests to prove that), then change it to a partitioned search if Instruments or Shark or timing the test cases confirms that a linear search is not fast enough.
Peter Hosey
(Previous comment was in response to a question of why not use binary search instead of linear.) I see this answer has picked up a downvote; would whoever gave it please explain why?
Peter Hosey
+2  A: 

Essentially what you are describing is Fuzzy Logic.

I wrote you a fuzzy logic rules class which should handle what you are wanting to do.

Features:

  • You can add your own custom rules easily with a method I've provided.
  • You can check a value with a single method and get a string result (or nil if it matches no rules).
  • As it uses rules you can define whatever interval periods you wish.

Add a new rule:

[logic addRule:@"2.0" forLowerCondition:14.5 forUpperCondition:17.0];

Sample output (from the code below):

Result for 15.20 is: 2.0

Here is the code implementation.....

In your Main:

FuzzyLogic *logic = [[FuzzyLogic alloc] initWithRule:@"1.0" forLowerCondition:10.0 forUpperCondition:14.5];

[logic addRule:@"2.0" forLowerCondition:14.5 forUpperCondition:17.0];
[logic addRule:@"2.5" forLowerCondition:17.0 forUpperCondition:23.0];

double input1 = 15.2f;

NSLog(@"Result for %.2lf is: %@", input1, [logic fuzzyResultForValue:input1]);

[logic release];

FuzzyLogic.h:

#import <Foundation/Foundation.h>

@interface FuzzyLogic : NSObject {
    NSMutableArray *conditions;
}

- (id) initWithRule:(NSString*)result forLowerCondition:(double)lower forUpperCondition:(double)upper;
- (void) addRule:(NSString*)result forLowerCondition:(double)lower forUpperCondition:(double)upper;
- (NSString*) fuzzyResultForValue:(double)input;

@end

FuzzyLogic.m:

#import "FuzzyLogic.h"

@implementation FuzzyLogic

enum {
    lowerIndex = 0,
    upperIndex,
    resultIndex
};

- (id) initWithRule:(NSString*)result forLowerCondition:(double)lower forUpperCondition:(double)upper {
    self = [super init];
    if (self != nil) {
        [self addRule:result forLowerCondition:lower forUpperCondition:upper];
    }
    return self;
}

- (void) addRule:(NSString*)result forLowerCondition:(double)lower forUpperCondition:(double)upper {

    NSArray *rule = [[NSArray alloc] initWithObjects:[NSString stringWithFormat:@"%lf",lower],[NSString stringWithFormat:@"%lf",upper],result,nil];

    if (conditions == nil) {
        conditions = [[NSMutableArray alloc] initWithObjects:rule,nil];
    } else {
        [conditions addObject:rule];
    }
}

- (NSString*) fuzzyResultForValue:(double)input
{
    NSString *returnable = nil;

    // Find the result
    for (NSArray *cond in conditions) {
        double lower = [[cond objectAtIndex:lowerIndex] doubleValue];
        double upper = [[cond objectAtIndex:upperIndex] doubleValue];
        if ( (input >= lower && input < upper) ) {
            returnable = [cond objectAtIndex:resultIndex];
            break;
        }   
    }

    if (returnable == nil) 
    { NSLog(@"Error: Input met no conditions!");}
    return returnable;
}

@end
Brock Woolf