Search code examples
phpphp-gde-notices

Notice error while counting number occurrences


I'm creating a simple .php script that

  1. Generates an array with random numbers (let's say 1000 integers)
  2. Outputs an image, where it displays all these numbers' arrangement on a scale from 0 to the largest number drawn

The output is an image (with given width), this is what I got:

image output example

As you can see, at some points the bars are higher. It's because the image usually isn't wide enough to present all the numbers, so if there are more numbers laying on one pixel, then the bar is going higher.

The problem: Sometimes this script works well, but occasionally I get numerous Notice errors:

Notice: Undefined offset: 398 in /.../index.php on line 61

Notice: Undefined offset: 125 in /.../index.php on line 61

Notice: Undefined offset: 192 in /.../index.php on line 61 ...

The script is below, some explanations:

  • Line 61 marked red (below)
  • function toScale() returns intval( number / maxNumber * scaleWidth )
  • inb4 unnecessary double declaration of $counts[num]=0, that's what I've done trying to solve the problem, it doesn't seem to change anything
  • $counts isn't used anywhere else in between these two parts
  • the number of Notice errors is never equal to the total numbers count, it's either a few or none of these.

code screenshot

Update: As Barmar mentioned, the second loop from my code can be replaced with

$max = max($data); $counts = array_count_values(array_map('toScale', $data));

It looks like just code simplification, but somehow this solves the problem. Why?


Solution

  • The problem is that you're scaling your numbers differently in the two loops.

    In the first loop, you're updating $max each time through the loop, and then calling toScale(data[$x]). Since toScale() depends on $max, some numbers will be scaled to a different maximum.

    In the second loop, everything is scaled to the final maximum. So for the same values of $data[$a] you'll get a different toScale($data[$a]) this time.

    You need to calculate the maximum of all the numbers before any calls to toScale. One way of doing this is by calculating the maximum in the loop that's generating all the random numbers:

    $max = 0;
    for ($a = 0; $a < $num; $a++) {
        $rand = rand();
        if ($rand > $max) {
            $max = $rand;
        }
        $data[] = $rand;
    }
    

    Or you can do it using a built-in function:

    $max = max($data);
    $counts = array_count_values(array_map('toScale', $data));