Сопоставление оплаты с суммами разных позиций (выпуск массива) - PullRequest
0 голосов
/ 30 июня 2019

это моя ситуация:

Я разрабатываю приложение для своей некоммерческой организации для отслеживания покупок в Интернете, в моей организации более 40 различных программ, финансируемых государством, и у нас чуть более 15 различных сайтов. Итак, как вы можете видеть из этого, это означает, что на одно здание приходится более 1 программы.

Таким образом, после совершения покупок (это может быть программа или сайт) мы должны отправлять отчеты источникам финансирования, и то, как они запрашивали эти отчеты, становилось беспорядком, особенно при покупке у Amazon, потому что один заказ может Оказывается, 5 или более различных сборов, и отчет должен соответствовать каждому из этих сборов в банковской выписке. Я создал код, чтобы найти нужные элементы, соответствующие каждому из обвинений, все работает отлично, но мой код не является пуленепробиваемым.

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

Вот весь код этой процедуры:

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim List() As Decimal = {49.03, 10.25, 4.6, 15.4, 30.15, 20.1, 20.1, 20.05, 20.25, 10.07, 9, 9, 9, 9, 9}
    Dim Nums() As Decimal = {59.5, 79.35, 106.15}
    Dim Sum1 As Decimal = 0
    Dim Sum2 As Decimal = 0
    For X = 0 To List.Length - 1
        Sum1 = Sum1 + List(X)
    Next

    For X = 0 To Nums.Length - 1
        Sum2 = Sum2 + Nums(X)
    Next
    If Sum1 = Sum2 Then
        StatementMatch(List, Nums)
    Else
        MsgBox("The values doesn't match." & vbCrLf & "The total price of all items is: $" & Sum1.ToString & vbCrLf & "The total of all payments is: $" & Sum2.ToString)
    End If
End Sub

В предыдущем коде я вручную загружал массив List (), который содержит цену каждого из элементов, а массив Nums () - это платежи, указанные в выписке по счету.

Прежде чем продолжить весь процесс, мне нужно убедиться, что общее количество товаров совпадает с суммой платежей.

Public Sub StatementMatch(List() As Decimal, Nums() As Decimal)
    Dim Test As String = ""
    Dim Tot As Decimal = 0
        Dim Result(((2 ^ List.Length) - 1), List.Length) As Decimal
        Dim Occur(,) As String

        For XX = 0 To Result.GetLength(0) - 1
            For Y = 0 To Result.GetLength(1) - 1
                Result(XX, Y) = 0
            Next
        Next


        Tot = 0

        For I = 0 To Result.GetLength(0) - 1
            Test = Convert.ToString(I, 2)

            Do While Test.Length < List.Length
                Test = "0" & Test
            Loop

            For M = Test.Length - 1 To 0 Step -1
                If Test.Substring(M, 1) = 1 Then
                    Tot = Tot + List(Test.Length - 1 - M)
                    Result(I, Test.Length - M) = 1
                End If
            Next

            Result(I, 0) = Tot
            Tot = 0
        Next


        Dim Count(Nums.Length - 1) As Integer
        For Z = 0 To Count.Length - 1
            Count(Z) = 0
        Next

        For XX = 0 To Result.GetLength(0) - 1
            For Y = 0 To Nums.Length - 1
                If Result(XX, 0) = Nums(Y) Then
                    Count(Y) = Count(Y) + 1
                End If
            Next
        Next

        Dim Big As Integer = 0

        For Z = 0 To Count.Length - 1
            If Big < Count(Z) Then
                Big = Count(Z)
            End If
        Next


        ReDim Occur(Nums.Length - 1, Big - 1)
        Dim CountD(Nums.Length - 1) As Integer
        For XX = 0 To CountD.Length - 1
            CountD(XX) = 0
        Next


        For XX = 0 To Occur.GetLength(0) - 1
            For Y = 0 To Occur.GetLength(1) - 1
                Occur(XX, Y) = "NONE"        'llenar el array con NONE
            Next
        Next

        Dim BinValue As String = ""

        For XX = 0 To Result.GetLength(0) - 1
            For Y = 0 To Nums.Length - 1
                If Result(XX, 0) = Nums(Y) Then
                    For Z = 1 To List.Length
                        BinValue = Result(XX, Z).ToString & BinValue
                    Next
                    Occur(Y, CountD(Y)) = BinValue
                    CountD(Y) = CountD(Y) + 1
                    BinValue = ""
                End If
            Next
        Next

        Dim Fill() As ULong = {0}

        ProcessValidResults(Occur, Occur.GetLength(0) - 1, Fill, True, List, Nums)

End Sub

Предыдущий код принимает 2 массива, затем я создал 3-й массив, который будет содержать все возможные результаты, называемый results (,) - это 2-мерный массив, в первом измерении он будет такой же длины, как и многие другие комбинации использовать числа в массиве List (), вторая ось будет такой же длины, как и элементы, которые у меня есть в списке + 1, в индексе 0 второй оси всегда будет результат комбинации, а остальные индексы будут иметь 0 или 1, чтобы сообщить нам, был ли использован этот элемент из List (). Все это делается в коде, для которого For I = 0.

После того, как это будет сделано, я создаю массив count (), который будет подсчитывать, сколько результатов соответствует каждому из Платежей, которые у нас есть в массиве Nums (), после этого я сравниваю, сколько результатов у меня есть для каждого один из платежей и возьмите один с наибольшим количеством результатов и поместите его в переменную Big. Это должно иметь размерность массива Occur (,). Поскольку каждый из платежей не имеет одинакового количества возможных комбинаций, я заполняю массив Occur значением «None», чтобы позже проверить, нет ли там комбинации. (Когда я пишу это, я придумываю способ поставить окончательное значение для пустых, которое я делаю позже, в коде, это ничего не меняет, просто сокращает шаги) Затем я создаю другой массив (массив наркоман, я думаю, что я стал) с именем CountD (), чтобы запомнить, какую позицию массива Occur я уже заполнил. В конце массив Occur (,) будет иметь столько строк (первое измерение), сколько платежей, и столько столбцов, сколько максимальное количество допустимых комбинаций для каждого платежа. Эти столбцы заполняются двоичным числом, которое будет соответствовать элементам списка ()

Наконец, я создаю массив Fill и отправляю его следующей функции, которая будет обрабатывать каждую из комбинаций, и вот здесь все может выйти из-под контроля.

Private Sub ProcessValidResults(Resultados(,) As String, Rows As Integer, Carry() As ULong, Attempt As Boolean, List() As Decimal, Nums() As Decimal)

    Dim Filler As ULong
    Dim POS As Integer = 0

    For Fill = 0 To List.Length - 1
        Filler = Filler * 10 + 1
    Next

    If Rows = 0 And Attempt = False Then
        Dim Final(Resultados.GetLength(1) * Carry.Length - 1) As ULong
        Dim Counter As Integer = 0
        For XX = 0 To Resultados.GetLength(1) - 1
            For Y = 0 To Carry.Length - 1
                If Resultados(Rows, XX) <> "NONE" Then
                    Final(Counter) = Convert.ToInt64(Resultados(Rows, XX)) + Carry(Y)
                Else
                    Final(Counter) = Filler + Carry(Y)
                End If
                Counter = Counter + 1
            Next
        Next

        For Z = 0 To Final.Length - 1
            If Final(Z) = Filler Then
                POS = Z + 1
                MsgBox(POS)
                Exit For
            End If
        Next

        FinalResults(Resultados, POS, List, Nums)
    End If


    If Rows = 1 Then
        Dim Valores1(Resultados.GetLength(1) - 1) As ULong
        Dim Valores2(Resultados.GetLength(1) - 1) As ULong

        For XX = 0 To Resultados.GetLength(1) - 1
            If Resultados(Rows - 1, XX) <> "NONE" Then
                Valores1(XX) = Convert.ToInt64(Resultados(Rows - 1, XX))
            Else
                Valores1(XX) = Filler
            End If
            If Resultados(Rows, XX) <> "NONE" Then
                Valores2(XX) = Convert.ToInt64(Resultados(Rows, XX))
            Else
                Valores2(XX) = Filler
            End If
        Next

        If Attempt = False Then


            Dim Sumas(Valores1.Length * Valores2.Length - 1) As ULong
            Dim Counter As Integer = 0
            For Val1 = 0 To Valores1.Length - 1
                For val2 = 0 To Valores2.Length - 1
                    Sumas(Counter) = Valores1(Val1) + Valores2(val2)
                    Counter = Counter + 1
                Next
            Next

            Dim Final((Convert.ToUInt64(Sumas.Length) * Convert.ToUInt64(Carry.Length) - 1)) As ULong
            Counter = 0
            For XX = 0 To Sumas.Length - 1
                For Y = 0 To Carry.Length - 1
                    Final(Counter) = Sumas(XX) + Carry(Y)
                    Counter = Counter + 1
                Next
            Next

            For Z = 0 To Final.Length - 1
                If Final(Z) = Filler Then
                    POS = Z + 1
                    MsgBox(POS)
                    Exit For
                End If
            Next
        Else
            Dim Final(Valores1.Length * Valores2.Length - 1) As ULong
            Dim Counter As Integer = 0
            For Val1 = 0 To Valores1.Length - 1
                For val2 = 0 To Valores2.Length - 1
                    Final(Counter) = Valores1(Val1) + Valores2(val2)
                    Counter = Counter + 1
                Next
            Next

            For Z = 0 To Final.Length - 1
                If Final(Z) = Filler Then
                    POS = Z + 1
                    MsgBox(POS)
                    Exit For
                End If
            Next
        End If
        FinalResults(Resultados, POS, List, Nums)
    End If

    If Rows >= 2 Then
        Dim Valores1(Resultados.GetLength(1) - 1) As ULong
        Dim Valores2(Resultados.GetLength(1) - 1) As ULong

        For XX = 0 To Resultados.GetLength(1) - 1
            If Resultados(Rows - 1, XX) <> "NONE" Then
                Valores1(XX) = Convert.ToInt64(Resultados(Rows - 1, XX))
            Else
                Valores1(XX) = Filler
            End If
            If Resultados(Rows, XX) <> "NONE" Then
                Valores2(XX) = Convert.ToInt64(Resultados(Rows, XX))
            Else
                Valores2(XX) = Filler
            End If
        Next


        Dim Sumas(Valores1.Length * Valores2.Length - 1) As ULong
        Dim Counter As Integer = 0
        For Val1 = 0 To Valores1.Length - 1
            For val2 = 0 To Valores2.Length - 1
                Sumas(Counter) = Valores1(Val1) + Valores2(val2)
                Counter = Counter + 1
            Next
        Next

        If Attempt = True Then
            ProcessValidResults(Resultados, Rows - 2, Sumas, False, List, Nums)
        Else
            Dim Final(Sumas.Length * Carry.Length - 1) As ULong
            Counter = 0
            For XX = 0 To Sumas.Length - 1
                For Y = 0 To Carry.Length - 1
                    Final(Counter) = Sumas(XX) + Carry(Y)
                    Counter = Counter + 1
                Next
            Next
            ProcessValidResults(Resultados, Rows - 2, Final, False, List, Nums)
        End If
    End If

End Sub

Функция:

ProcessValidResults(Resultados(,) As String, Rows As Integer, Carry() As ULong, Attempt As Boolean, List() As Decimal, Nums() As Decimal)

- рекурсивная функция, которая будет вызывать себя до тех пор, пока не завершит обработку каждой из комбинаций. Массив Results (,) такой же, как Occur (,) из предыдущей функции, то есть с комбинациями для каждого платежа. Строки сообщают нам сумму платежей, это будет определять способ обработки информации, Carry () это новый массив, который будет содержать результаты после каждого прохода этой функции. Попытайтесь просто сообщить нам, выполняется ли функция в первый раз, и с этим мы узнаем, если у нас уже есть значения, которые были обработаны, и нам нужно включить каждый раз, массив List () для создания заполнителя для замены None, который был ранее сохранен в Occur (,), а также для отправки его следующей функции, которая даст нам элементы, используемые для каждого числа, то же самое для массив Nums (), просто для переноса на следующую функцию.

Вот что я делаю, я конвертирую эти двоичные числа в произойти (,) в целое число (прямое преобразование, 111 - это 111, а не 7), затем я начинаю добавлять числа каждой строки со следующей и сохранять их в другом одномерном массиве. Затем функция вызовет сама себя, возьмет этот новый массив и обработает элементы с элементами из другой строки, поместит их в новый массив и снова вызовет себя, пока не завершит обработку всех строк.

В конце массив Final () будет существовать до тех пор, пока существуют комбинации из элементов массива Occur (,). Если мы говорим, что массив Occur равен (7,30), то массив Final будет Final (30 ^ 7), и здесь все развалится, потому что у меня будет больше возможных комбинаций, чем элементов в массиве.

Я сделал это так, потому что тогда я могу пройти через Final () и проверить, что его значение заполнено на 1, например, Final (123) = 1111111 (это число меняется, в нем столько цифр, сколько элементов включено). List ()) Затем, зная, что у индекса есть правильный ответ, я обработаю его, чтобы узнать, какие элементы List () были использованы для сопоставления с каждым из чисел в Nums ().

Private Sub FinalResults(Occur(,) As String, POS As Integer, List() As Decimal, Nums() As Decimal)

    Dim X As Integer = Occur.GetLength(0)
    Dim Y As Integer = Occur.GetLength(1)
    Dim FinalPos(Occur.GetLength(0) - 1) As Integer

    Dim Expo As Integer
    For T = 1 To Occur.GetLength(0)
        Expo = Y ^ (X - T)
        FinalPos(T - 1) = Math.Ceiling(POS / Expo)
        POS = POS - (Expo * (FinalPos(T - 1) - 1))

    Next

    Dim Txt As String = ""

    For Y = 0 To Occur.GetLength(0) - 1
        Txt = Txt & (Y + 1).ToString & " ==> "
        For X = 0 To Occur.GetLength(1) - 1
            Txt = Txt & Occur(Y, X) & " - "
        Next
        Txt = Txt & vbCrLf
    Next
    Txt = Txt & vbCrLf
    For Z = 0 To FinalPos.Length - 1

        Txt = Txt & FinalPos(Z).ToString & " ==> " & Occur(Z, FinalPos(Z) - 1) & vbCrLf
    Next

    Txt = Txt & vbCrLf & vbCrLf

    For X = 0 To List.Length - 1
        Txt = Txt & List(X) & " - "
    Next

    Txt = Txt & vbCrLf & vbCrLf
    For Y = 0 To Nums.Length - 1
        Txt = Txt & Nums(Y) & " = "
        For Z = 0 To List.Length - 1

            If Occur(Y, FinalPos(Y) - 1).ToString.Substring(Z, 1) = "1" Then
                Txt = Txt & List(List.Length - 1 - Z).ToString & " + "
            End If
        Next
        Txt = Txt & vbCrLf
    Next

    MsgBox(Txt)
End Sub

Вот так я обрабатываю позицию в массиве Final () и превращаю ее в элементы, используемые для соответствия каждому из платежей. Начиная со строки:

Dim Txt As String = ""

Это просто показать в MsgBox результаты для целей тестирования. В конце комбинация между массивами Occur (,) и FinalPos () даст мне правильный ответ, Occur (,) найдет возможные комбинации для каждого платежа, а FinalPos () скажет, какой из них тот, который мне нужно использовать для каждого платежа.

Теперь дело в том, что мне нужно найти альтернативу массиву, который может содержать еще больше и больше элементов, другой вариант - изменить что-то в конфигурации, чтобы массивы содержали больше элементов, или найти другой способ решить эту проблему. проблема.

Если вы хотите проверить код, просто создайте форму с помощью кнопки, и это подойдет. Вы можете вручную добавить больше предметов и больше платежей для целей тестирования.

Спасибо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...