def _balance_load(k, nworkers, seq=(lambda t: 2*t)):
    """
    Distribute the workload as evenly as possible among the available
    processes. The tasks to be distributed are the calculation of each T_{j,1}
    for j=1...k.  The number of steps required to compute T_{j,1} is given by
    seq(j).  This approach to load-balancing can be more performant than
    relying on dynamic load-balancing.


    Each process is given a list of 2-tuples (j,seq(j)) so that the maximal sum
    of seq(j)'s (workloads) for any one process is minimized.
    The algorithm used here is not optimal for all possible sequences, but is
    typically optimal.  Basically, it gives one task to each process in some
    order, then reverses the list of processes and repeats.

    Parameters
    ----------
    k  : int
        Number of extrapolation steps to be performed (i.e., number of T_{j,1} values)
    nworkers : int
        Number of processes over which to distribute jobs
    seq  : callable(i), int i>=1
        Sequence of step numbers

    Returns
    -------
    j_nj_list : list of lists of 2-tuples
        The list contains nworkers lists, each one containing which T_{j,1}
        values each processor has to calculate (each T_{j,1} is specified by
        the tuple (j,nj)).
    """
    if k <= nworkers:  # Just let each process compute one of the T_{j,1}
        j_nj_list = [[(j,seq(j))] for j in range(k, 0, -1)]
    else:
        j_nj_list = [[] for i in range(nworkers)]
        processes = range(nworkers)
        i = k
        while 1:
            if i >= nworkers:
                for j in processes:
                    j_nj_list[j] += [(i, seq(i))]
                    i -= 1
            else:
                for j in processes:
                    if i == 0:
                        break
                    j_nj_list[j] += [(i, seq(i))]
                    i -= 1
                break
            processes = processes[::-1]

    return j_nj_list