CVXPY, чтобы минимизировать разницу между двумя массивами - PullRequest
1 голос
/ 08 мая 2020

У меня есть 2 массива по n элементов в каждом. Первый - это процент (от 0% до 100%), при котором сумма всех элементов дает 100%. Второй составлен из целых чисел от -3 до 3 (включительно).

Цель моего сценария состоит в том, чтобы изменять числа ровно настолько, чтобы получить суммарный продукт, равный примерно 0.

Здесь это то, что у меня есть до сих пор, и, похоже, работает:

import numpy as np
import cvxpy as cp

# Emulated user inputs
n = 10
arr1 = np.random.rand(n)
arr1 = arr1/np.sum(arr1)
arr2 = np.random.randint(-3, 3, n)
while not (-0.1 < np.sum(np.dot(arr1, arr2)) < 0.1):  # User ensures this condition before running
    arr2 = np.random.randint(-3, 3, n)

print("Initial array       =", arr2)
print("Initial sum product =", np.sum(np.dot(arr1, arr2)), "\n")


# Gradually increase allowable element-wise delta
for delta_limit in range(2, 7):  # Variation of 2 is acceptable for first iteration
    # Create variable for new array
    arr3 = cp.Variable(len(arr2), integer=True)

    # Define constraints
    constraints = [-0.000050001 <= arr3 * arr1,  # Not working, but it's supposed to target sum product ~0
                   arr3 * arr1 <= 0.000049999,   # Not working, but it's supposed to target sum product ~0
                   -3 <= arr3,                   # Lower bound of -3
                   arr3 <= 3,                    # Upper bound of +3
                   -delta_limit <= arr2 - arr3,  # Restrict element-wise difference by less than +/-delta_limit
                   arr2 - arr3 <= delta_limit]   # Restrict element-wise difference by less than +/-delta_limit

    # Objective function (minimize variation between arr3 and arr2)
    objective = cp.Minimize(cp.multiply((1 / (len(arr2) - 1)), cp.sum(cp.power((arr3 - arr2) - arr2.mean(), 2))))

    # Solve
    solution = cp.Problem(objective, constraints).solve()

    # Check if a solution was found
    if arr3.value is None and delta_limit < 6:
        continue
    elif arr3.value is None and delta_limit == 6:
        print("No solution found, try another combination")
    else:
        # Change float to integer
        arr3.value = arr3.value.astype(int)

        # Print results
        print("New array           =", arr3.value)
        print("New sum product     =", np.sum(np.dot(arr1, arr3.value)), "\n")
        print("Differences         =", arr3.value - arr2)
        print("Total difference    =", np.round(np.sum(np.absolute(arr3.value - arr2))))
        break

Это, например, напечатает следующее:

Initial array       = [ 1  1  1 -2  2  2 -2  0 -2 -2]
Initial sum product = -0.05254026276509359 

New array           = [ 0  0  0 -2  1  1 -2  0 -1 -2]
New sum product     = -0.3703945724305102 

Differences         = [-1 -1 -1  0 -1 -1  0  0  1  0]
Total difference    = 6

Однако я хотел бы добавить еще 1 ограничение : «знак элементов в первом массиве должен быть сохранен в новом массиве». Я бы предпочел сделать это как ограничение, а не только для проверки l oop.

Я пробовал такие вещи безуспешно:

a) np.sign(arr2) == np.sign(arr3)
b) np.sign(arr2) == cp.multiply(arr3 + 0.1, cp.power(cp.power(arr3 + 0.1, 2, -0.5))

И многое другое, я просто могу не взломать ... :( Кроме того, если у вас есть предложения по улучшению, я очень открыт для них.

Заранее спасибо!

1 Ответ

0 голосов
/ 08 мая 2020

Я предлагаю вам следующий код.

import statistics as st
import math

array = [ 1, 1, 1, -2, 2, 2, -2, 0, -2, -2]
new = []


def getSearchRange(e):
    if e > 0:
        search_in = range(0, 3)
    elif e < 0:
        search_in = range(-3, 0)
    else:
        search_in = range(-3, 3)
    # ...
    return search_in

def sum_check(new, search_in):
    result = []
    store_num =[]
    for n in search_in:
        if new:
            result.append(math.fabs(st.mean(new) + n/len(new)))
        else:
            result.append(math.fabs(n))
        store_num.append(n)
    index = result.index(min(result))
    # ...
    return store_num[index]


for e in array:
    search_in = getSearchRange(e)
    new.append(sum_check(new, search_in))

print(new)   

он дает мне решение, которое, кажется, соответствует тому, что вы ищете? :

[0, 0, 0, -1, 1, 0, -1, 1, -1, -1] (в среднем 0,2)

Примечание: если вы действительно хотите варьировать number в выводе вы можете использовать функцию randomint для ввода 2 или 3 разных чисел (n) в процессе сходимости. Но оптимальным решением кажется то, что дает сценарий.

import random
...
store_num.append(random.randint(-3, 3)) # 2 or 3 times```
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...