взвешивание плоского списка до нормального распределения

У меня есть список строковых элементов любой длины, мне нужно «нормализовать» этот список так, чтобы каждый элемент был частью нормального распределения, добавляя вес к строке.

Что является более эффективным и математическим / статистическим способом, кроме того, что я имею ниже?

func normalizeAppend(in []string, shuffle bool) []string {
    var ret []string

    if shuffle {
        shuffleStrings(in)
    }

    l := len(in)
    switch {
    case remain(l, 3) == 0:
        l3 := (l / 3)
        var low, mid, high []string
        for i, v := range in {
            o := i + 1
            switch {
            case o <= l3:
                low = append(low, v)
            case o > l3 && o <= l3*2:
                mid = append(mid, v)
            case o >= l3*2:
                high = append(high, v)
            }
        }

        q1 := 1600 / len(low)
        q2 := 6800 / len(mid)
        q3 := 1600 / len(high)

        for _, v := range low {
            ret = append(ret, fmt.Sprintf("%s_%d", v, q1))
        }

        for _, v := range mid {
            ret = append(ret, fmt.Sprintf("%s_%d", v, q2))
        }

        for _, v := range high {
            ret = append(ret, fmt.Sprintf("%s_%d", v, q3))
        }
    case remain(l, 2) == 0 && l >= 4:
        l4 := (l / 4)
        var first, second, third, fourth []string
        for i, v := range in {
            o := i + 1
            switch {
            case o <= l4:
                first = append(first, v)
            case o > l4 && o <= l4*2:
                second = append(second, v)
            case o > l4*2 && o <= l4*3:
                third = append(third, v)
            case o > l4*3:
                fourth = append(fourth, v)
            }
        }
        q1 := 1600 / len(first)
        q2 := 3400 / len(second)
        q3 := 3400 / len(third)
        q4 := 1600 / len(fourth)

        for _, v := range first {
            ret = append(ret, fmt.Sprintf("%s_%d", v, q1))
        }

        for _, v := range second {
            ret = append(ret, fmt.Sprintf("%s_%d", v, q2))
        }

        for _, v := range third {
            ret = append(ret, fmt.Sprintf("%s_%d", v, q3))
        }

        for _, v := range fourth {
            ret = append(ret, fmt.Sprintf("%s_%d", v, q4))
        }
    default:
        var first, second, third []string
        q1 := (1 + math.Floor(float64(l)*.16))
        q3 := (float64(l) - math.Floor(float64(l)*.16))
        var o float64
        for i, v := range in {
            o = float64(i + 1)
            switch {
            case o <= q1:
                first = append(first, v)
            case o > q1 && o < q3:
                second = append(second, v)
            case o >= q3:
                third = append(third, v)
            }
        }
        lq1 := 1600 / len(first)
        lq2 := 3400 / len(second)
        lq3 := 1600 / len(third)
        for _, v := range first {
            ret = append(ret, fmt.Sprintf("%s_%d", v, lq1))
        }

        for _, v := range second {
            ret = append(ret, fmt.Sprintf("%s_%d", v, lq2))
        }

        for _, v := range third {
            ret = append(ret, fmt.Sprintf("%s_%d", v, lq3))
        }

    }

    return ret
}

Некоторые просили разъяснений:

У меня есть список элементов, которые будут выбраны из списка много раз по одному за раз путем взвешенного выбора, для начала у меня есть список с (подразумеваемым) весом 1:

[a_1, b_1, c_1, d_1, e_1, f_1, g_1, h_1, i_1, j_1, k_1]

Я ищу лучший способ сделать этот список чем-то, производящим более «нормальное» распределение веса для выбора:

[a_1, b_2, c_3, d_5, e_14, f_30, g_14, h_5, i_3, j_2, k_1]

или, возможно, мне нужно изменить свои методы на что-то более обоснованное статистически. Суть в том, что я хочу контролировать выбор из списка элементов многими способами, одним из которых здесь является обеспечение того, чтобы элементы возвращались способом, аппроксимирующим нормальную кривую.

1 ответ

  1. Если вы просто хотите рассчитать веса для данного списка, то вам нужно следующее:

    • Среднее нормального распределения
    • Дисперсия нормального распределения
    • Дискретизатор значений

    Первый довольно прост. Вы хотите, чтобы среднее было в центре списка. Таким образом (предполагая индексацию на основе нуля):

    mean = (list.size - 1) / 2
    

    Второй вид произвольный и зависит от того, насколько крутой вы хотите, чтобы ваши веса упали. Веса нормального распределения практически равны нулю за пределами расстояния 3 * standard_deviationот mean. Таким образом, хорошее стандартное отклонение в большинстве случаев, вероятно, является чем-то между четвертой и шестой длиной списка:

    standard_deviation = (1/4 .. 1/6) * list.size
    variance = standard_deviation^2
    

    Предполагая, что вы хотите целочисленные веса, необходимо дискретизировать веса из нормального распределения. Самый простой способ сделать это-указать максимальный вес (элемента в средней позиции).

    Вот и все. Вес элемента в положенииi:

    weight[i] = round(max_weight * exp(-(i - mean)^2 / (2 * variance)))