Учитывая 2 массива Array1 = {a,b,c...n}
и Array2 = {10,20,15....x}
как я могу генерировать всю возможную комбинацию как Строки (i) b (j) c (k) n (p) где
1 <= i <= 10, 1 <= j <= 20 , 1 <= k <= 15, .... 1 <= p <= x
Такой как:
a1 b1 c1 .... n1
a1 b1 c1..... n2
......
......
a10 b20 c15 nx (last combination)
Таким образом во всем общем количестве комбинации = продукт элементов array2 = (10 X 20 X 15 X ..X x)
Подобный Декартову произведению, в котором второй массив определяет верхний предел для каждого элемента в первом массиве.
Пример с постоянными числами,
Array x = [a,b,c]
Array y = [3,2,4]
Таким образом, мы будем иметь 3*2*4 = 24 комбинации. Результаты должны быть:
a1 b1 c1
a1 b1 c2
a1 b1 c3
a1 b1 c4
a1 b2 c1
a1 b2 c2
a1 b2 c3
a1 b2 c4
a2 b1 c1
a2 b1 c2
a2 b1 c3
a2 b1 c4
a2 b2 c1
a2 b2 c2
a2 b2 c3
a2 b2 c4
a3 b1 c1
a3 b1 c2
a3 b1 c3
a3 b1 c4
a3 b2 c1
a3 b2 c2
a3 b2 c3
a3 b2 c4 (last)
using System;
using System.Text;
public static string[] GenerateCombinations(string[] Array1, int[] Array2)
{
if(Array1 == null) throw new ArgumentNullException("Array1");
if(Array2 == null) throw new ArgumentNullException("Array2");
if(Array1.Length != Array2.Length)
throw new ArgumentException("Must be the same size as Array1.", "Array2");
if(Array1.Length == 0)
return new string[0];
int outputSize = 1;
var current = new int[Array1.Length];
for(int i = 0; i < current.Length; ++i)
{
if(Array2[i] < 1)
throw new ArgumentException("Contains invalid values.", "Array2");
if(Array1[i] == null)
throw new ArgumentException("Contains null values.", "Array1");
outputSize *= Array2[i];
current[i] = 1;
}
var result = new string[outputSize];
for(int i = 0; i < outputSize; ++i)
{
var sb = new StringBuilder();
for(int j = 0; j < current.Length; ++j)
{
sb.Append(Array1[j]);
sb.Append(current[j].ToString());
if(j != current.Length - 1)
sb.Append(' ');
}
result[i] = sb.ToString();
int incrementIndex = current.Length - 1;
while(incrementIndex >= 0 && current[incrementIndex] == Array2[incrementIndex])
{
current[incrementIndex] = 1;
--incrementIndex;
}
if(incrementIndex >= 0)
++current[incrementIndex];
}
return result;
}
Альтернативное решение:
Шаг первый: прочитайте мою серию статей о том, как генерировать все строки, соответствующие контекстно-зависимой грамматике:
http://blogs.msdn.com/b/ericlippert/archive/tags/grammars/
Шаг второй: определите грамматику, которая генерирует нужный вам язык. Например, вы можете определить грамматику:
S: a A b B c C
A: 1 | 2 | 3
B: 1 | 2
C: 1 | 2 | 3 | 4
Очевидно, что вы можете легко сгенерировать эту строку определения грамматики из ваших двух массивов. Затем введите ее в код, который генерирует все строки данной грамматики, и все готово; вы получите все возможности. (Не обязательно в том порядке, в котором вы хотите их видеть)
.Я не хочу предоставлять вам полный исходный код. Итак, вот идея.
Вы можете сгенерировать элементы следующим образом:
Я предполагаю A = (a1, a2, ..., an)
и B = (b1, b2, ... , bn)
(поэтому A
и B
содержат n
элементов).
Тогда сделайте это рекурсивно! Напишите метод, который принимает A
и B
и выполняет ваши функции:
Если A
и B
содержат только один элемент (называемый
или bn
), просто выполните итерацию от 1 до bn
и соедините и
с вашей повторяющейся переменной.
Если A
и B
содержат более одного элемента, захватите первые элементы ( a1
или b1
), выполните итерацию от 1 до bn
и выполните для каждого шага итерации:
A
и B
, начиная со второго элемента, то есть A '= (a2, a3, ..., an)
или B' = (b2, b3, ..., bn)
. Для каждого элемента, сгенерированного рекурсивным вызовом, объедините a1
, итерационную переменную и сгенерированный элемент из рекурсивного вызова.Здесь вы можете найти аналогичный пример того, как генерировать вещи на C #, вам «просто» нужно адаптировать его к вашим потребностям.
Если я правильно понимаю, вам нужно что-то вроде декартова произведения . Если это так, вот как это можно сделать с помощью LINQ. Возможно, нет точного ответа, но попытайтесь понять идею
char[] Array1 = { 'a', 'b', 'c' };
string[] Array2 = { "10", "20", "15" };
var result = from i in Array1
from j in Array2
select i + j;
Эти статьи могут помочь
Конечно. Это немного сложно сделать с LINQ, но, безусловно, возможно, используя только стандартные операторы запросов.
ОБНОВЛЕНИЕ: Это тема моего блога в понедельник, 28 июня 2010 г. ; спасибо за отличный вопрос. Кроме того, один из комментаторов моего блога отметил, что существует еще более элегантный запрос, чем тот, который я дал. Я обновлю код здесь, чтобы использовать его.
Сложность состоит в том, чтобы составить декартово произведение произвольного количества последовательностей. По сравнению с этим "застегивание" в письмах тривиально. Вы должны изучить это, чтобы убедиться, что вы понимаете, как это работает. Каждая часть достаточно проста, но способ их объединения требует некоторого привыкания:
static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>()};
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from accseq in accumulator
from item in sequence
select accseq.Concat(new[] {item})
);
}
Чтобы объяснить, как это работает, сначала поймите, что делает операция "накопление". Самая простая операция накопления - это «сложить все в этой последовательности вместе». Вы делаете это так: начните с нуля. Для каждого элемента в последовательности текущее значение аккумулятора равно сумме элемента и предыдущего значения аккумулятора. Мы делаем то же самое, за исключением того, что вместо накопления суммы на основе суммы на данный момент и текущего элемента, мы накапливаем декартово произведение по мере продвижения.
Мы собираемся сделать это, воспользовавшись тем фактом, что у нас уже есть оператор в LINQ, который вычисляет декартово произведение двух вещей:
from x in xs
from y in ys
do something with each possible (x, y)
Повторно беря декартово произведение аккумулятора на следующий элемент во входной последовательности и немного склеив результаты, мы можем по ходу сгенерировать декартово произведение.
Так что подумайте о стоимости аккумулятора.В иллюстративных целях я собираюсь показать значение аккумулятора в виде результатов операторов последовательности, которые он содержит. На самом деле аккумулятор не содержит этого. Фактически аккумулятор содержит операторы , которые производят эти результаты. Вся операция здесь просто строит массивное дерево операторов последовательности, результатом которого является декартово произведение. Но окончательное декартово произведение фактически не вычисляется, пока запрос не будет выполнен. В иллюстративных целях я покажу, каковы результаты на каждом этапе пути, но помните, что на самом деле они содержат операторы , которые производят эти результаты.
Предположим, мы берем декартово произведение последовательности последовательностей {{1, 2}, {3, 4}, {5, 6}}
. Накопитель начинается как последовательность, содержащая одну пустую последовательность: {{}}
При первом накоплении аккумулятор равен {{}}, а элемент - {1, 2}. Мы делаем это:
from accseq in accumulator
from item in sequence
select accseq.Concat(new[] {item})
Итак, мы берем декартово произведение {{}}
на {1, 2}
, и для каждой пары мы объединяем: У нас есть пара ({}, 1)
, поэтому мы объединяем {}
и {1}
, чтобы получить {1}
. У нас есть пара ({}, 2})
, поэтому мы объединяем {}
и {2}
, чтобы получить {2}
. Следовательно, в результате мы имеем {{1}, {2}}
.
Таким образом, при втором накоплении аккумулятор - {{1}, {2}}
, а элемент - {3, 4}
. Опять же, мы вычисляем декартово произведение этих двух последовательностей, чтобы получить:
{({1}, 3), ({1}, 4), ({2}, 3), ({2}, 4)}
, а затем из этих элементов объединяем вторую последовательность с первой. Итак, результатом является последовательность {{1, 3}, {1, 4}, {2, 3}, {2, 4}}
, что нам и нужно.
Теперь мы снова накапливаем. Мы берем декартово произведение аккумулятора на {5, 6}
, чтобы получить
{({ 1, 3}, 5), ({1, 3}, 6), ({1, 4}, 5), ...
, а затем объединяем второй элемент с первым, чтобы получить:
{{1, 3, 5}, {1, 3, 6}, {1, 4, 5}, {1, 4, 6} ... }
, и все готово. Мы накопили декартово произведение.
Теперь, когда у нас есть функция полезности, которая может брать декартово произведение произвольного количества последовательностей, остальное легко сравнить:
var arr1 = new[] {"a", "b", "c"};
var arr2 = new[] { 3, 2, 4 };
var result = from cpLine in CartesianProduct(
from count in arr2 select Enumerable.Range(1, count))
select cpLine.Zip(arr1, (x1, x2) => x2 + x1);
И теперь у нас есть последовательность последовательностей строк, по одной последовательности строк на строку:
foreach (var line in result)
{
foreach (var s in line)
Console.Write(s);
Console.WriteLine();
}
Легко и просто!
FinalResult - это искомый массив. Предполагается, что оба массива имеют одинаковый размер.
char[] Array1 = { 'a', 'b', 'c' };
int[] Array2 = { 3, 2, 4 };
var finalResult = new List<string>();
finalResult.Add(String.Empty);
for(int i=0; i<Array1.Length; i++)
{
var tmp = from a in finalResult
from b in Enumerable.Range(1,Array2[i])
select String.Format("{0} {1}{2}",a,Array1[i],b).Trim();
finalResult = tmp.ToList();
}
Думаю, этого будет достаточно.
Для сравнения, вот способ сделать это с помощью Python
from itertools import product
X=["a", "b", "c"]
Y=[3, 4, 2]
terms = (["%s%s"%(x,i+1) for i in range(y)] for x,y in zip(X,Y))
for item in product(*terms):
print " ".join(item)