Rayann Nayran
Rayann Nayran

Reputation: 1135

How to organize in chunks a not accurate division?

I have a number of participants and a number of groups, and I have to organize the participants into groups.

Example:

10/3 = 3, 3 and 4.
10/9 = 2,2,2 and 4.
23/3 = 6,6,6 and 5.

I have tried with array_chunk using the size paramether as a rounded result of participants/groups but it Did not work well.

Edit with my problem solved.

    $groups           = $this->request->data['phases_limit'];
    $classified_lmt   = $this->request->data['classified_limit'];

    $participants     = count($game->user_has_game);

    $participants_lmt = floor($participants / $groups);
    $remainders       = $participants % $groups;

    if ($groups > $participants) {
        throw new \Exception("Há mais grupos que participantes");
    }

    for ($i=0; $i < $groups; $i++) {
        $p = $this->Phase->newEntity();
        $p->name             = 'Grupo #' . $game->id;
        $p->game_id          = $game->id;
        $p->classified_limit = $classified_lmt;
        $this->Phase->save($p);

        // add the number of participants per group
        for ($j=0; $j < $participants_lmt; $j++) {
            $user_has_game = array_pop($game->user_has_game);
            $g                   = $this->Phase->GroupUserHasGame->newEntity();
            $g->group_id         = $p->id;
            $g->user_has_game_id = $user_has_game->id;
            $this->Phase->GroupUserHasGame->save($g);
        }

        // check if it is the last iteration
        if (($groups - 1) == $i) {
            // add the remainders on the last iteration
            for ($k=0; $k < $remainders; $k++) {
                $user_has_game  = array_pop($game->user_has_game);
                $g                   = $this->Phase->GroupUserHasGame->newEntity();
                $g->group_id         = $p->id;
                $g->user_has_game_id = $user_has_game->id;
                $this->Phase->GroupUserHasGame->save($g);
            }
        }
    }

Upvotes: 0

Views: 39

Answers (2)

fubar
fubar

Reputation: 17398

Have you tried the modulus operator? It gives you the remainder after dividing the numerator by the denominator.

For example, if you want to split 10 people into 3 groups:

floor(10 / 3) = 3; // people per group
10 % 3 = 1; // 1 person left over to add to an existing group.

Edit - I included the following function as part of my original answer. This doesn't work for OP, however I want to leave it here, as it may help others.

function group($total, $groups) 
{
    // Calculate participants per group and remainder
    $group = floor($total / $groups);
    $remainder = $total % $groups;

    // Prepare groupings and append remaining participant to first group
    $groupings = array_fill(0, $groups, $group);
    $groupings[0] += $remainder;

    return $groupings;
}

Upvotes: 2

Josep Valls
Josep Valls

Reputation: 5560

Not sure there are off-the-shelf libraries for that. I just implemented something similar in Java if you need some ideas:

  public List<Integer> createDistribution(int population_size, int groups) {
        List<Integer> lst = new LinkedList();
        int total = 0;
        for (double d : createDistribution(groups)) {
            // this makes smaller groups first int i = new Double(population_size * d).intValue();
            int i = (int)Math.round(population_size * d);
            total += i;
            lst.add(i);
        }
        // Fix rounding errors
        while (total < population_size) {
            int i = r.nextInt(groups);
            lst.set(i, lst.get(i) + 1);
            total += 1;
        }
        while (total > population_size) {
            int i = r.nextInt(groups);
            if (lst.get(i) > 0) {
                lst.set(i, lst.get(i) - 1);
                total -= 1;
            }
        }
        return lst;
    }

Upvotes: 0

Related Questions