Can this code be optimized?

I'm interested in speed, not good code, so I'm using an array, not a list (integer).

I have an array similar to: 0,1,0,1,1,0,1,0,1,1,1,0,0,1

I am interested in the position of each number, so I can later pick one at random.

so what I do is loop through the array to take the position number of each 1 and then create a new array like this: 2,4,5,7,9,10,11,14

bitwise can be used here? I have no idea

the code looks like this:

Private Function theThing() As Integer()
    Dim x As Integer
    'arIn() would be a parameter
    Dim arIn() As Integer = {0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1}
    Dim ar() As Integer = Nothing
    Dim arCount As Integer = -1

    For x = 1 To arIn.GetUpperBound(0)
        If arIn(x) = 1 Then
            arCount += 1
        End If
    Next

    If arCount > -1 Then
        'using redim preseve is slower than the loop above
        ReDim ar(arCount)

        arCount = 0
        For x = 1 To arIn.GetUpperBound(0)
            If arIn(x) = 1 Then
                ar(arCount) = x
                arCount += 1
            End If
        Next
    End If

    Return ar
End Function

      

* EDIT *

current solution (10-15% faster) now

Private Function theThing() As Integer
    Dim ar() As Integer = {0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1}
    Dim arLenght As Integer = ar.GetUpperBound(0)
    Dim arCount As Integer = 0
    Dim x As Integer

    For x = 1 To arLenght
        If ar(x) = 1 Then
            ar(arCount) = x
            arCount += 1
        End If
    Next

    dim r As New Random()

    Return ar(r.Next(arCount))
End Function

      

I don't think it can be more optimized than that unless someone finds a way to do exactly what the solution does, but faster.

Before this question, my whole business was able to do about 25,500 for every 10 seconds.

Now it can do over 32250 all the time, 21% more, thanks!

+1


source to share


11 replies


A few tips for the original algorithm:

  • Try storing the arIn.GetUpperBound (0) results in a variable. I don't know how VB makes it loops, but chances are the function is being called once on each iteration. You should check it out.
  • That which If arCount > -1

    will always be true. Delete it.

If you want to keep the same I / O, then I don't think there is much that can be improved.



Now, if you want a function that makes random selection, then this might be a little better. I will write in C # since I know it better. You must be able to understand:

public static int GetRandomSetBit(int[] AllBits)
{
    // Perhaps check here if AllBits is null/empty. I'll skip that for now.

    int L = AllBits.Length;
    int SetBitCount = 0;

    // No point to save a few bytes here. Also - you can make a global array
    // large enough for all occasions and skip allocating it every time.
    // In that case consider automatic resizing and watch out for
    // multithreading issues (if you use several threads).
    int[] GoodPositions = new int[L];

    for ( int i = 0; i < L; i++ )
        if ( AllBits[i] != 0 )
        {
            GoodPositions[SetBitCount] = i;
            SetBitCount++;
        }
     Random r = new Random(); // Should use one global instance
     return GoodPositions[r.Next(SetBitCount)];
}

      

I'm afraid it won't get better. Not unless you can change the inputs / outputs or requirements in any way.

+2


source


Instead of storing an array of integers, why not put them all in one whole?

oldArray = [0, 1, 1, 0, 1]
newValue =  22     (binary 10110)

      

If you want to check if a particular bit position is set, perform a bitwise comparison with two on the strength of that position:



is position 2 set?
value:    10110
4 (2^2):  00100
result:   00100 --> true

is position 0 set?
value:    10110
1 (2^0):  00001
result:   00000 --> false

      

Do a bitwise comparison search and you should find a lot of help.

Here are some good questions that might help:
What are bitwise operators? How do you set, clear, and switch one bit?

+8


source


I find it hard to believe that the backup will be slower than your loop if it is not inside a loop.

In this case, for raw speed, don't count the number 1 in arIn to set the size of ar. Since ar can never be larger than arIn, just set it to the same size and store it at the end at the end (won't be slower as it is outside the loop and will always be trimming, not expanding - Hopefully VB can do it in place rather than allocating more memory). Also, the size of the arIn cache in case VB calculates it every time through the loop (probably if ReDim is enabled).

Private Function theThing() As Integer()
    Dim x As Integer
    'arIn() would be a parameter
    Dim arIn() As Integer = {0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1}
    Dim ar(arIn.GetUpperBound(0)) As Integer
    Dim arCount As Integer
    Dim arInCount As Integer

    arCount = 0
    arInCount = arIn.GetUpperBound(0)
    For x = 1 To arInCount
        If arIn(x) = 1 Then
            ar(arCount) = x
            arCount += 1
        End If
    Next

    ReDim Preserve ar(arCount)
    Return ar
End Function

      

Alternatively, you can completely remove the redim command if you slightly change what is returned. Make the returned array larger than the input array, and use the first element to manipulate whatever portions of the array you choose at will.

For your sample, the returned array would be:

{8,2,4,5,7,9,10,11,14,?,?,?,?,?,?} (? values are irrelevant).
 ^ <-------+--------> <----+---->
 |         |               |
 |         |               +-- These are unused.
 |         |
 |         +-- These are used.
 |
 +-- This is the count of elements to use.

      

This code will:

Private Function theThing() As Integer()
    Dim x As Integer
    'arIn() would be a parameter
    Dim arIn() As Integer = {0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1}
    Dim ar(arIn.GetUpperBound(0)+1) As Integer
    Dim arCount As Integer
    Dim arInCount As Integer

    arCount = 0
    arInCount = arIn.GetUpperBound(0)
    For x = 1 To arInCount
        If arIn(x) = 1 Then
            ar(arCount) = x
            arCount += 1
        End If
    Next

    ar(0) = arCount
    Return ar
End Function

      

Then in your code that selects a random value from ar instead of:

rndval = rnd(ar.GetUpperBound)

      

using:

rndval = rnd(ar(0) + 1)

      

+1


source


Disable overflow checking.
 Project Properties -> Compilation -> Advanced Compilation Options -> Remove Integer Integrity Checks.
If you need overflow checking for the rest of the project, you can move the code to a new project (like a DLL) and disable overflow checking for that new project.

Also make sure you are running a release build (optimization enabled) and you are not debugging it.

EDIT: I get 8.5s (12s if I declare an array inside the For parameter that I use for testing) to call the function 50 million times. If you only get 32000, either you are using very large inputs or something is slowing down your code. For example, if you count the time inside a program and run it in a profiler, you will get incorrect results because profiling can slow down the program dramatically. Also glitches like these Slow method calls can impact performance.

+1


source


I think when Recursive quoted BitArray, he meant something like this:

using System.Collections.Generic;
using System;
using System.Collections;

class Program
{
    static void Main(string[] args)
    {
        var input = new BitArray(new byte[] { 0x5A /*01011010b*/
                                        , 0xE4 /*11100100b*/ });
        var output = new List<int>();
        var offset = 1;
        foreach (var bit in input)
        {
            if (bit)
            {
                output.Add(offset);
            }
            offset++;
        }
    }
}

      

+1


source


Speed ​​for the number of items?

Changing items?

Compile time or run time?

What are the spatial constraints?

A well-known strategy is to take a few of them and write down all combinations and their results: 0000 β†’ 0001 β†’ 4 0010 β†’ 3 0011 β†’ 3.4 0100 β†’ 2 0101 β†’ 2.4 0110 β†’ 2.3 ...

Why do you want to convert from this binary representation to pick a random bit? This is unlikely to help in performance. Better group them by 8 bits, use a table telling you how many 1s are in the group, and repeat 5 times. then you know how many there are. make a random selection, then choose the selected one.

0


source


You may find that using the For Each parameter and a list initialized with at least the length of the input array is faster than an index:

If arIn.Length > 0 Then
  Dim lstOut As New List(Of Integer)(arIn.Length)
  Dim ix As Integer = 0
  For Each val As Integer In arIn
    If val = 1 Then
      lstOut.Add(ix)
    End If
    ix = ix + 1
  End If
  Return lstOut.ToArray()
Else
  Return Nothing
End If

      

But you will have to test it.

0


source


This is what BitArray did .

0


source


Perhaps you can compress the best performance using a lookup table. Then apply the following algorithm:

Sorry I'm not talking about VB anymore, so I'll have to write C #. Here is a snippet of all the code, since the entire lookupTable will contain 256 elements.

using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        var input = new byte[] { 0x5A /*01011010b*/, 0xE4 /*11100100b*/ };
        var output = new List<int>();
        var lookupTable = new int[][] { new int[0] /*00000000b*/
                                           , new int[] { 8 }    /*00000001b*/
                                           , new int[] { 7 }    /*00000010b*/
                                           , new int[] { 7,8 }  /*00000011b*/
                                           , new int[] { 6 }    /*00000100b*/
                                           , new int[] { 6,8 }  /*00000101b*/
                                           , new int[] { 6,7,8 }  /*00000111b*/
                                          };
        int offset = 0;
        foreach (var value in input)
        {
            foreach (var position in lookupTable[(int)value])
            {
                output.Add(position + offset);
            }
            offset += 8;
        }
    }
}

      

0


source


If it is easy to create a prime number slightly larger than the length of your array (depending on its size, it may or may not be easy), and you don't care about a completely uniform random, then you can generate a random number in that range and generate this cycle. It should find the answer within a few iterations (depending on the density 1s). Source code in C # since I don't remember the vb syntax:

int RandomInArray(int[] ar)
{
    int p = GenerateSlightlyLargerPrime(ar.Length);

    int x = random.nextInt(p);
    int i = x;
    do
    {
       if (i < ar.Length && ar[i] == 1)
           return i;
       i = (i + x) % p;
    } while (i != x);
    return -1;
}

      

Note that this is not 100% uniformly random, but it should be pretty close.

0


source


I've tried a different approach. The idea is to save a random entry and exit when an entry is found that matches 1. This solution is not ideal because some random are not used, which may or may not break randomness. In addition, the speed is highly dependent on the density "1". The code looks like this:

public static int GetRandomSetBit(int[] AllBits)
{
    int L = AllBits.Length;
    Random r = new Random();    // Better use a global instance as this hurt performance a lot
    do
    {
         selected = r.Next(L);

    } while (AllBits[selected] == 0);
    return selected;
}

      

On my PC, if the Random object instantiation is moved to global, it can run 50,000,000 probes in about 11 seconds if their value is 5 "1" out of 30. If there is up to 15 "1" the time it takes is reduced to about 5 from.

Compared to the code suggested by Vilx, its code can run 50,000,000 probes on my PC in about 13 seconds

0


source







All Articles