Вы должны войти, чтобы создавать сообщения и темы.

Вывод списка массивов [Решено]

Всем привет! Имеется такая задача, в которой нужно вывести список массивов, состоящий из 4 элементов, каждый элемент которого может принимать некоторое число от 1 до 8. При этом числа в массиве не должны повторяться между собой.

Пример

1 2 3 4 
1 2 3 5 
1 2 3 6 
1 2 3 7 
1 2 3 8 
1 2 4 3 
1 2 4 5 
1 2 4 6 
1 2 4 7 
1 2 4 8 
1 2 5 3 
1 2 5 4 
1 2 5 6 
1 2 5 7 
1 2 5 8 
...
int[] main_arr = { 1, 1, 1, 0 };        

async void add()
{
    await Task.Run(() =>
    {
        double countdown = Math.Pow(NumBase, power);
        while ((int)countdown != 0)
        {
            for (int i = main_arr.Length - 1; i >= 0; i--)
            {
                if (main_arr[i] == 8)
                {
                    main_arr[i] = 1;
                }
                else
                {
                    main_arr[i] += 1;
                    break;
                }
            }

            if (!isRepeat(main_arr))
            {
                print(main_arr);
            }
            countdown--;

        }
    });
}

void print(int[] array)
{

    for (int i = 0; i < array.Length; i++)
    {
        richTextBox1.Text += $"{array[i]} ";

    }
    richTextBox1.Text += "\n";
}


Второе условие задачи, нужно чтобы сумма двух первых и двух последних чисел совпадала. Если совпадает, то выводим массив. 
Пример кода

        bool isEqual(int[] array)
        {
            int left_sum = 0;
            int right_sum = 0;

            for (int i = 0; i < array.Length / 2; i++)
            {
                left_sum += array[i];
            }

            for (int i = array.Length / 2; i < array.Length; i++)
            {
                right_sum += array[i];
            }

            return left_sum == right_sum;
        }
        ...
        if (!isRepeat(main_arr))
        {
            if (isEqual(main_arr))
            {
                print(main_arr);
            }
        }


Вроде бы всё работает, но не пойму как убрать повторения массивов?

1 7 3 5
1 7 5 3 //у обоих массивов сумма левой и правой части одинакова

Как сделать такой вывод массива без учёта перестановок? Пробовал добавлять массивы в СкопированоList<int[]>,каждый раз сортировать новый массив и делать посимвольное сравнение из этого общего списка, но программа тогда останавливается и ничего не выводит вовсе.

Приветствую. На вскидку это все делается в один запрос средствами Linq. Дубликаты удаляются через Distinct(), а другие условия можно через Where() сделать.

Цитата: Adler от 13.09.2022, 14:45

Приветствую. В теории это все делается в одну строку через Linq. Дубликаты удаляются через Distinct(), а другие условия можно через Where() сделать.

Как правильно применить такую операцию? И можно ли вообще применять Distinct() к двумерному массиву?

Возможно я не совсем верно понял задачу, чуть позже вникну...

 
Цитата: Вячеслав от 13.09.2022, 14:34

Всем привет! Имеется такая задача, в которой нужно вывести список массивов, состоящий из 4 элементов, каждый элемент которого может принимать некоторое число от 1 до 8. При этом числа в массиве не должны повторяться между собой.

Привет Вячеслав.  Лично мне сложновато понять условия по такому описанию. Я могу понять примерно такое условие "Имеется такая задача, в которой нужно вывести список массивов. Массивы состоят из 4 элементов. Элемент массива это число от 1 до 8 включительно. Необходимо отсортировать массивы по двум условиям. Первое числа в массиве не должны повторяться между собой. Второе нужно чтобы сумма двух первых и двух последних чисел совпадала." Для такой формулировки могу предложить примерно такое решение.

Входные данные

internal static List<IEnumerable<int>> ListArrayInt = new List<IEnumerable<int>>()
       {
           new int[4]{ 1, 2, 3, 4 },
           new int[4]{ 1, 2, 3, 5 },
           new int[4]{ 1, 2, 3, 6 },
           new int[4]{ 1, 2, 3, 7 },
           new int[4]{ 1, 2, 3, 8 },
           new int[4]{ 1, 2, 4, 3 },
           new int[4]{ 1, 2, 4, 5 },
           new int[4]{ 1, 2, 4, 6 },
           new int[4]{ 1, 2, 4, 7 },
           new int[4]{ 1, 2, 4, 8 },
           new int[4]{ 1, 2, 5, 3 },
           new int[4]{ 1, 2, 5, 4 },
           new int[4]{ 1, 2, 5, 6 },
           new int[4]{ 1, 2, 5, 7 },
           new int[4]{ 1, 2, 5, 8 },
           new int[4]{ 1, 7, 3, 5 },
           new int[4]{ 1, 7, 5, 3 },
       };

Сортировка

ListArrayInt.Where(x =>
            {
                int[] array1 = x.ToArray()[0..2];
                int[] array2 = x.ToArray()[2..];
                if (array1.Distinct().Count() < array1.Length || array2.Distinct().Count() < array2.Length) return false;
                if (array1.Sum() != array2.Sum()) return false;
                else return true;
            }).ToList().ForEach(x => Console.WriteLine(string.Concat(x.Select(x => x.ToString()).Select(x => x.Replace(x,$" {x} ")))));


Я не тестировал на нормальных входных данных. Если условия другие уточните, ох уж эти формулировки.🙂

 

Лайкнул Владислав
Владислав
Цитата: Viktor от 13.09.2022, 17:30
 
Цитата: Вячеслав от 13.09.2022, 14:34

Всем привет! Имеется такая задача, в которой нужно вывести список массивов, состоящий из 4 элементов, каждый элемент которого может принимать некоторое число от 1 до 8. При этом числа в массиве не должны повторяться между собой.

Привет Вячеслав.  Лично мне сложновато понять условия по такому описанию. Я могу понять примерно такое условие "Имеется такая задача, в которой нужно вывести список массивов. Массивы состоят из 4 элементов. Элемент массива это число от 1 до 8 включительно. Необходимо отсортировать массивы по двум условиям. Первое числа в массиве не должны повторяться между собой. Второе нужно чтобы сумма двух первых и двух последних чисел совпадала."

В примере под спойлером, где 1 2 3 4... это то, что выводит код ниже - это входные данные. Затем они обрабатываются вторым условием, то есть сумма правой и левой части должна совпадать. Но проблема в другом, как удалить дубликаты с одинаковыми суммами с обоих сторон, при этом оставив разные числа для каждого массива. Сортировать список не нужно, это я пытался таким образом посимвольно сравнивать элементы для каждого массива в списке, чтобы узнать, если ли этот массив уже там. 

Что выводит программа сейчас

1 4 2 3 
1 4 3 2 
1 5 2 4 
1 5 4 2 
1 6 2 5 
1 6 3 4 
1 6 4 3 
1 6 5 2 
1 7 2 6 
1 7 3 5 
1 7 5 3 
1 7 6 2 
1 8 2 7 
1 8 3 6 
1 8 4 5 
1 8 5 4 
1 8 6 3 
1 8 7 2 
2 3 1 4 
2 3 4 1 
2 4 1 5 
2 4 5 1 
2 5 1 6 
2 5 3 4 
2 5 4 3 
2 5 6 1 
2 6 1 7 
2 6 3 5 
2 6 5 3 
2 6 7 1 
2 7 1 8 
2 7 3 6 
2 7 4 5 
2 7 5 4 
2 7 6 3 
2 7 8 1 
2 8 3 7 
2 8 4 6 
2 8 6 4 
2 8 7 3 
3 2 1 4 
3 2 4 1 
3 4 1 6 
3 4 2 5 
3 4 5 2 
3 4 6 1 
3 5 1 7 
3 5 2 6 
3 5 6 2 
3 5 7 1 
3 6 1 8 
3 6 2 7 
3 6 4 5 
3 6 5 4 
3 6 7 2 
3 6 8 1 
3 7 2 8 
3 7 4 6 
3 7 6 4 
3 7 8 2 
3 8 4 7 
3 8 5 6 
3 8 6 5 
3 8 7 4 
4 1 2 3 
4 1 3 2 
4 2 1 5 
4 2 5 1 
4 3 1 6 
4 3 2 5 
4 3 5 2 
4 3 6 1 
4 5 1 8 
4 5 2 7 
4 5 3 6 
4 5 6 3 
4 5 7 2 
4 5 8 1 
4 6 2 8 
4 6 3 7 
4 6 7 3 
4 6 8 2 
4 7 3 8 
4 7 5 6 
4 7 6 5 
4 7 8 3 
4 8 5 7 
4 8 7 5 
5 1 2 4 
5 1 4 2 
5 2 1 6 
5 2 3 4 
5 2 4 3 
5 2 6 1 
5 3 1 7 
5 3 2 6 
5 3 6 2 
5 3 7 1 
5 4 1 8 
5 4 2 7 
5 4 3 6 
5 4 6 3 
5 4 7 2 
5 4 8 1 
5 6 3 8 
5 6 4 7 
5 6 7 4 
5 6 8 3 
5 7 4 8 
5 7 8 4 
5 8 6 7 
5 8 7 6 
6 1 2 5 
6 1 3 4 
6 1 4 3 
6 1 5 2 
6 2 1 7 
6 2 3 5 
6 2 5 3 
6 2 7 1 
6 3 1 8 
6 3 2 7 
6 3 4 5 
6 3 5 4 
6 3 7 2 
6 3 8 1 
6 4 2 8 
6 4 3 7 
6 4 7 3 
6 4 8 2 
6 5 3 8 
6 5 4 7 
6 5 7 4 
6 5 8 3 
6 7 5 8 
6 7 8 5 
7 1 2 6 
7 1 3 5 
7 1 5 3 
7 1 6 2 
7 2 1 8 
7 2 3 6 
7 2 4 5 
7 2 5 4 
7 2 6 3 
7 2 8 1 
7 3 2 8 
7 3 4 6 
7 3 6 4 
7 3 8 2 
7 4 3 8 
7 4 5 6 
7 4 6 5 
7 4 8 3 
7 5 4 8 
7 5 8 4 
7 6 5 8 
7 6 8 5 
8 1 2 7 
8 1 3 6 
8 1 4 5 
8 1 5 4 
8 1 6 3 
8 1 7 2 
8 2 3 7 
8 2 4 6 
8 2 6 4 
8 2 7 3 
8 3 4 7 
8 3 5 6 
8 3 6 5 
8 3 7 4 
8 4 5 7 
8 4 7 5 
8 5 6 7 
8 5 7 6 


Как мне удалить такие вот дубликаты? 
Спойлер

...
8 4 5 7 
8 4 7 5 //дубликат массива выше
8 5 6 7 
8 5 7 6 //дубликат массива выше

У них просто 2 последних числа поменялись местами, и так почти во всем списке.

Я плоховат в LINQ, но это вроде работает.

Код

.Select(x =>
            {
                int[] ar = x.ToArray();
                return ($"{ar[0]}{ar[1]}", $"{ar[2]}{ar[3]}");
            });

            IEnumerable<(string, string)> resDistinc = res2.DistinctBy(x => x.Item1).DistinctBy(x => x.Item2);

            var res1 = (resDistinc.Select(x => x.Item1.ToCharArray().Select(x => int.Parse(x.ToString()))), resDistinc.Select(x=> x.Item2.ToCharArray().Select(x => int.Parse(x.ToString()))));
            
            var retRes = new List<int[]>();
            for (int i = 0; i < res1.Item1.Count(); i++)
            {
                retRes.Add(new int[] { res1.Item1.ToArray()[i].ToArray()[0], res1.Item1.ToArray()[i].ToArray()[1], res1.Item2.ToArray()[i].ToArray()[0], res1.Item2.ToArray()[i].ToArray()[1] }); 
            }

 

P.S. А почему нету кнопки со спойлером?

Добрый день.

P.S. А почему нету кнопки со спойлером?

Это какой-то глюк движка форума.
Есть такой вариант: формируем пост без спойлера, публикуем и сразу редактируем. В режиме редактирования кнопка спойлера есть всегда.
В режиме ответа кнопка спойлера то есть, то нет, какой-то глюк движка форума. 

Голова дана человеку не для того, чтобы кушать, а для того, чтобы думать.
Цитата: Viktor от 13.09.2022, 23:02

Я плоховат в LINQ, но это вроде работает.

ругается на res2. Откуда это?

Оффтоп

В firefox нету спойлера, а в edge есть 🤷‍♂️

Это результат прошлой сортировки из прошлого поста, только тут строчку почистил т.к фигню написал. Логика тут такая. ↓

Картинка

Просто есть методы в LINQ, которые позволяют это сделать проще. Я пока мало знаю.

Приветствую.
Если нет никаких ограничений по производительности, можно решить задачу полностью на LINQ. Проверка уникальности и удаление дубликатов реализовывается через HashSet коллекцию, которая не может содержать повторяющиеся элементы. В принципе, можно и обычный List использовать, только с дополнительной проверкой существования элемента в коллекции. Ещё один вариант – использовать метод расширения Distinct из LINQ с собственной логикой сравнения.
Уверен, что некоторые методы можно реализовать проще, но надеюсь направлю в нужном направлении, если, конечно, условие понял правильно 🙃

Листинг

public class ProblemSolver
{
    private readonly HashSet<int> _hashSet = new HashSet<int>();

    public List<IEnumerable<int>> GetSets()
    {
        return Enumerable.Range(1000, 9000)
            .Select(ConvertToSetOfDigits)
            .Where(DigitsInRange)
            .Where(UniqueDigits)
            .Where(EqualSides)
            .Where(DistinctPermutations)
            .ToList();
    }

    private IEnumerable<int> ConvertToSetOfDigits(int number)
    {
        return number.ToString().Select(c => int.Parse(c.ToString()));
    }

    private bool DigitsInRange(IEnumerable<int> set)
    {
        return set.All(digit => 1 <= digit && digit <= 8);
    }

    private bool UniqueDigits(IEnumerable<int> set)
    {
        return set.ToHashSet().Count == 4;
    }

    private bool EqualSides(IEnumerable<int> set)
    {
        return set.Take(2).Sum() == set.TakeLast(2).Sum();
    }

    private bool DistinctPermutations(IEnumerable<int> set)
    {
        var leftSide = set.Take(2).OrderBy(digit => digit);
        var rightSide = set.TakeLast(2).OrderBy(digit => digit);

        return _hashSet.Add(leftSide.Concat(rightSide).Aggregate((digit, acc) => acc += digit * 10));
    }
}

@farmv, теперь ругается на DistinctBy, говорит не знает такого метода, предлагает заменить на Distinct, но тогда ругается на Item1, мол нельзя преобразовать лямбда выражение. У меня Net Core 3, если что.

@dapofig, ограничений по памяти нет. Задача вроде бы решена сортировкой каждой последовательности из списка и приведение её к String, но вариант с выводом не устроил. Да, условия по сумме левой и правой части соблюдаются циклом перед выводом, но визуально это не воспринимается. 

Привет!
Генерацию входной последовательности (двумерного массива для тестирования) и фильтрацию с условиями сделал через методы расширения:

Методы расширения

using System;
using System.Collections.Generic;
using System.Linq;
using static System.Linq.Enumerable;

namespace ConsoleApp
{
    public static class ExtendedEnumerable
    {
        /// <summary>
        /// Разбивает последовательность на равные по количеству подпоследовательности.
        /// </summary>
        /// <param name="numberOfSubSequences">Количество подпоследовательностей</param>
        /// <exception cref="ArgumentException"></exception>
        public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, int numberOfSubSequences)
        {
            var sourceCount = source.Count();

            if (numberOfSubSequences < 1)
            {
                throw new ArgumentException($"Не возможно разбить последовательность на {numberOfSubSequences} равных подпоследовательностей!");
            }

            if (sourceCount % numberOfSubSequences != 0)
            {
                throw new ArgumentException($"Не возможно разбить последовательность размером {sourceCount} на {numberOfSubSequences} равных подпоследовательностей!");
            }

            var subSequences = new T[numberOfSubSequences][];
            var sequenceCount = sourceCount / numberOfSubSequences;
            var sourceEnumerator = source.GetEnumerator();

            while (sourceEnumerator.MoveNext())
            {
                for (var i = 0; i < numberOfSubSequences; i++)
                {
                    var part = new T[sequenceCount];

                    for (var j = 0; j < sequenceCount; j++)
                    {
                        part[j] = sourceEnumerator.Current;
                        sourceEnumerator.MoveNext();
                    }

                    subSequences[i] = part;
                }
            }

            return subSequences;
        }

        /// <summary>
        /// Фильтрует последовательность по заданным условиям.
        /// </summary>
        /// <param name="conditions">Последовательность условий фильтрации</param>
        /// <returns></returns>
        public static IEnumerable<T> Filter<T>(this IEnumerable<T> source, IEnumerable<Predicate<T>> conditions)
        {
            return source.Where((item) => conditions.Aggregate(true, (result, condition) => result && condition(item)));
        }

        /// <summary>
        /// Генерирует все возможные перестановки элементов последовательности с повторениями.
        /// </summary>
        /// <param name="selectionCount">Количество элементов выборки</param>
        /// <exception cref="ArgumentException"></exception>
        public static IEnumerable<IEnumerable<T>> PermutationsWithRepetition<T>(this IEnumerable<T> source, int selectionCount)
        {
            var sourceCount = source.Count();

            if (selectionCount < 0)
            {
                throw new ArgumentException("Количество элементов не может быть меньше нуля!");
            }

            if (selectionCount == 0)
            {
                return Empty<IEnumerable<T>>();
            }

            if (selectionCount == 1)
            {
                return source.Split(sourceCount);
            }

            var permutationsCount = (int)Math.Pow(sourceCount, selectionCount);
            var permutations = new List<IEnumerable<T>>(permutationsCount);

            foreach (var item in source)
            {
                foreach (var placement in PermutationsWithRepetition(source, selectionCount - 1))
                {
                    permutations.Add(placement.Prepend(item));
                }
            }

            return permutations;
        }

        /// <summary>
        /// Генерирует все возможные перестановки элементов последовательности без повторений.
        /// </summary>
        /// <param name="selectionCount">Количество элементов выборки</param>
        /// <exception cref="ArgumentException"></exception>
        public static IEnumerable<IEnumerable<T>> PermutationsWithoutRepetition<T>(this IEnumerable<T> source, int selectionCount)
        {
            return PermutationsWithRepetition(source, selectionCount).Where((sequence) => sequence.Distinct().Count() == sequence.Count());
        }
    }
}

Для отсеивания дубликатов сделал Comparer, только не уверен насчёт метода генерации хеш кода, но с задачей вроде справляется.

IntSequenceComparer

using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp
{
    public class IntSequenceComparer : IEqualityComparer<IEnumerable<int>>
    {
        /// <summary>
        /// Сравнивает последовательности без учёта порядка элементов.
        /// </summary>
        public bool Equals(IEnumerable<int> first, IEnumerable<int> second)
        {
            return first.Except(second).SequenceEqual(Enumerable.Empty<int>());
        }

        /// <summary>
        /// Генерирует уникальное значение последовательности без учёта порядка элементов.
        /// </summary>
        public int GetHashCode(IEnumerable<int> sequence)
        {
            return sequence.Sum() * sequence.Count();
        }
    }
}

Ну и, собственно, использование этого всего:

Main метод

using System;
using System.Collections.Generic;
using System.Linq;
using static System.Console;

namespace ConsoleApp
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            const int numberOfParts = 2;
            const int selectionCount = 4;
            const int min = 1;
            const int max = 9;

            // Условия фильтрации
            var conditions = new Predicate<IEnumerable<int>>[]
            {
                // Элементы не повторяются.
                (collection) => collection.Distinct().Count() == collection.Count(),

                // Суммы всех (в нашем случае двух) частей равны
                (collection) => collection.Split(numberOfParts).GroupBy((item) => item.Sum()).Count() == 1,
            };

            // Генерация входной последовательности
            var inputSequence = Enumerable.Range(min, max - min).PermutationsWithRepetition(selectionCount);

            // Фильтрация входной последовательности
            var outputSequence = inputSequence.Filter(conditions).Distinct(new IntSequenceComparer());

            Print(outputSequence);
            ReadKey();
        }

        private static void Print<T>(IEnumerable<T> collection)
        {
            foreach (var item in collection)
            {
                Write($"{item} ");
            }

            WriteLine();
        }

        private static void Print<T>(IEnumerable<IEnumerable<T>> collection)
        {
            foreach (var item in collection)
            {
                Print(item);
            }
        }
    }
}


Результат

Лайкнули Arman и BlendLog
ArmanBlendLog
Цитата: Вася от 14.09.2022, 22:44

Привет!
Генерацию входной последовательности (двумерного массива для тестирования) и фильтрацию с условиями сделал через методы расширения...

Благодарю за помощь, это то, что нужно!