1

i am developing an android application right now. and i got a problem with one important method in my app, because i can't make best algorithm to equate many inputs to many data.

Here's the scenario: The method's input is a coordinates from override method onTouchEvent(), so the input will be so many when the screen is touched and the finger is moved on it. i have to equate that so-many-coordinate to 24 values in array. the values in array is a coordinate as well. So when the input has the same value as values in arrays, it scores a point.

here's the code that i used:

public void checkCoordinate(float x, float y){
    int sensitivity = 30; 
    for(int z = 0; z < 24; z++){
        if(x > (cxy[z][0]-sensivity) && x < (cxy[z][0]+sensivity) && y > (cxy[z][1]-sensivity) && y < (cxy[z][1]+sensivity)){
            points += 1; 
            Log.i("CheckCoordinate", "Total Points = "+points); 
    }
}

i have 2 ways to equating it. one, using the algorithm above or, using 24 if to check every input. but i don't think that 2 ways is good in this case. so i need your help if you had the same case as mine and you have solved that or you have a better or best solution please tell me.

thank you in advance. sorry if my english is bad.

abuybuy
  • 799
  • 2
  • 16
  • 33
  • You need to look at this : http://stackoverflow.com/questions/245509/algorithm-to-tell-if-two-arrays-have-identical-members – Kazekage Gaara Jun 18 '12 at 14:37
  • So ultimately you are trying to see if your finger is within 1 of 24 regions, each region defined by a single point, and the sensitivity value? Also, what is the problem here, is it slower than you would like, does it not function, etc...??? – trumpetlicks Jun 18 '12 at 16:21
  • @Kazekage Gaara - I dont think that algo applies. He isnt looking to see if they are identical members or not. If you look at his current code, he is trying to see if his finger is within 1 of 24 different regions. – trumpetlicks Jun 18 '12 at 16:23
  • @trumpetlicks The OP says : _i have to equate that so-many-coordinate to 24 values in array. the values in array is a coordinate as well. So when the input has the same value as values in arrays, it scores a point._ . So I believe he is going for comparing every member in arrays. – Kazekage Gaara Jun 18 '12 at 16:29
  • @Kazekage Gaara - Look at his algorithm, each region has essentially a 60 by 60 region, not a simplistic point. I think that he has asked question incorrectly, but his code based algorithm speaks for itself. – trumpetlicks Jun 18 '12 at 16:32
  • @trumpetlicks maybe I see your point. And I'm starting to feel you are right, but the OP shall clarify further. – Kazekage Gaara Jun 18 '12 at 16:46
  • @Kazekage Gaara - Exactly, I think he already has the correct answer but if it is performance he is looking for, then there may be better ways of implementing what he wants!!! – trumpetlicks Jun 18 '12 at 16:50
  • @trumpetlicks i have not found the correct answer. what are u said above is correct, my question is not correct but my code has explain it. i think to use 24 if to check each inputs. what do you think guys? – abuybuy Jun 19 '12 at 01:32

2 Answers2

1

With this being said, your generic idea is correct. The question is can it now be optimized somehow?

There are many methods we can potentially employ here. The trick is all in the way you arrange your data. In your current case, you are doing a center point with your sensitivity variable being your span. One could imagine the first optimization being instead of using a sensitivity that moves left right up and down from a center point, you could implement instead a top-left point with a span that only moves right and down from the top-left point. Your if statement would then become:

if(x > cxy[z][0] && x < (cxy[z][0]+sensivity) && y > cxy[z][1] && y < (cxy[z][1]+sensivity))

So what does this do for you: This above optimization allows you to hold the same overall amount of data but get rid of 2 math operations per check. Considering that your input parameters are both floating points, this can save quite a bit of time.

If you are doing all pixel based operations, then that brings me to the next optimization. Do ALL calculation using integers instead of floating point, this will also heavily speed up your overall algorithm time.

a further optimization could now be, if you are willing to spend more RAM for better performance, you could instead of having 1 point per region, you could have a top-left and a bottom-right point for each region. This would make your if statement look something like the following:

if(x > tlxy[z][0] && x < brxy[z][0] && y > tlxy[z][1] && y < brxy[z][1])

where tlxy is the array of top-left points, brxy is the array of bottom-right points
and z is still the "region" you are checking against

How does this aid: As you can see in the above if statement, this now has absolutely no explicit math operations. To support this algorithm, you need to be willing to spend 2 times the memory as the array cxy originally was.

Now within your loop, you are going through ALL 24 region points. If you KNOW FOR CERTAIN that none of your regions overlap, then a point can truly only fall in 1 region at a time. You can save some time on most x y input points by breaking out of the for loop at the point you also increment points. That would look as follows:

public void checkCoordinate(float x, float y){
    for(int z = 0; z < 24; z++){
        if(x > tlxy[z][0] && x < brxy[z][0] && y > tlxy[z][1] && y < brxy[z][1]){
            points += 1; 
            break;
        }
    }
}

The above will ONLY WORK IFF you know for certain there are no overlapping regions (not even edges.

On final optimization I can see might have potential. Depending on what your regions look like, you could pre-separate all regions into quadrants. This way you can test the x point to be on the left or right hand of the screen, then test the y point to be in the top or bottom. If your regions distribution is fairly even, this could have the potential to cut your test time down by 4 as you would not only need to test the regions within a quadrant (if the given statistical distribution is what I said). In the worst case, all regions lie in a single quadrant and all points you test are in that quadrant in which case the problem from a complexity standpoint is no worse than what it was before. It simply adds a setup test on your input x and y.

I hope this gives you enough info to at least get started on your way!!!

trumpetlicks
  • 7,033
  • 2
  • 19
  • 33
  • Wow, i never thought about using break in if and the calculation using float is heavier than integer. i've tried and it works fine in AVD, and i believe it will be smoother in device. Thank you very much! – abuybuy Jun 19 '12 at 04:11
  • No problem, I hope it REALLY does work well in your device, keep having fun :-) – trumpetlicks Jun 19 '12 at 04:42
0

For 24 values, this might be an overkill, but you might also want to consider using a plain-array hash table (with open addressing collision resolution):

  1. For simplicity, let's say you use the y value to get the jump position (if regions are well separated vertically, this should decrease the number of checks). Presuming that your screen resolution is 600x800, you can divide y by 60 to get ~13 slots. If you are using a 8-bit table, that equals to ~18 items per slot (round(800/60)=13, round(255/13)=18).

  2. To speed up calculations, you should keep everything round and simple, so you can get the slot number using:

    int yi = (int)y;
    
    // this is your "hash function". depending on your actual data,
    // you might want to modify it to get a lesser chance of collisions
    byte slot = (byte)((yi / 60) * 18);
    
  3. Now that you have the slot index, simply jump to the hash table and check until there are no more items to check:

    rectangle r;
    int yi = (int)y;
    for (byte slot=(byte)(yi / 26); slot < 256; slot++)
    {
        r = hashtable[slot];
    
        // is this an empty slot?
        if (r.brxy == 0)
           break;
    
        // perform exact check
        if (r.left < x && x < r.right && 
            r.top < y && y < r.bottom)
           break;
    }
    
  4. Hash table needs to be created during init in a similar way: for each of your 24 regions, calculate its hash (slot) index. If the position in the hash table is occupied, simply increase by 1 until you find an empty spot. NOTE: will you will have to add each region to all overlapping slots. A simplest way would be to add it to slots s, s-1 and s+1.

Your loop currently does 12 lookups on average, while a hash-based approach performs a single hash calculation and should require only two or three lookups on average (hash tables are said to have O(1) complexity on average, presuming a good hash function).

Example

Your hashtable should ideally look something like:

hashtable[0]: rectangle(0, 0, 60, 60);
hashtable[1]: rectangle(20, 20, 80, 80);
hashtable[2]: (empty)
hashtable[3]: (empty)
...
// next slot starts at [18]
hashtable[18]: rectangle(20, 20, 80, 80); // this region is in slots 0 and 1
hashtable[19]: rectangle(30, 70, 90, 130);
hashtable[20]: rectangle(400, 70, 460, 130);
hashtable[21]: (empty)
...

So, if your touch point is (430, 100), calculation will continue as follows:

a) slot = (byte)((100/60) * 18) = 18;
b) check hashtable[18], overlapping? no
c) check hashtable[19], overlapping? no
c) check hashtable[20], overlapping? yes, found after 3 checks

Performance only depends on the chosen hash-function:

If you have many items with similar x coordinates, you might get many collisions in some slots: that is why it's important to choose a good hash function. If regions are fixed, you can even create a perfect hash.

vgru
  • 49,838
  • 16
  • 120
  • 201