Моделирование процесса считалки с помощью LINQ
Стоит задача смоделировать процесс считалки. Есть коллекция необходимо удалять элементы через одного до тех пор, пока в ней не останется один элемент. Нельзя просто удалять только четные/нечетные элементы. Т.к. после первого обхода всех элементов, на втором круге четные могут смениться нечетными и наоборот. На примере: если в коллекции 5 элементов и мы удалять начинаем с 1-ого, тогда удаляются: 1-ый, 3-ий. После 3-его мы пропускаем 4-ый и переходим опять на начало, удаляем уже 0-ой (а не 1-ый). И так далее, пока не останется один элемент в колекции.
Еще решение ограничено тем, что колекция передаётся в качестве ICollection, и изменить нужно именно входной параметр. Метод не возвращает параметров, void. Я реализовал решение, но хочу понять как решить эту задачу с помощью LINQ.
Моя реализация:
private static void RemoveEachSecondItem<T>(ICollection<T> collection)
{
int num = 1;
while (collection.Count != 1)
{
foreach (var a in collection.ToArray())
{
if (num % 2 == 0)
{
collection.Remove(a);
}
num++;
}
PrintHumans(collection);
}
}
И вот что у меня получилось с использованием LINQ:
private static void RemoveEachSecondItem<T>(ICollection<T> collection)
{
int num = 0;
while (collection.Count != 1)
{
var a = collection.Where(p => ++num % 2 == 0);
foreach (var i in a.ToArray())
collection.Remove(i);
PrintHumans(collection);
}
}
Собственно, как можно сделать лучше с использованием LINQ?
Ответы (1 шт):
Просто оставлю это здесь.
Задача легко решается аналитически. Для этого нужно просто взглянуть на последовательность индексов элементов для разных длин коллекции.
for (int i = 1; i < 200; i++)
{
var a = Enumerable.Range(0, i).ToList();
RemoveEachSecondItem(a);
Console.WriteLine(i + "=" + a[0]);
}
1=0
2=0
3=2
4=0
5=2
6=4
7=6
8=0
9=2
10=4
11=6
12=8
13=10
14=12
15=14
16=0
17=2
18=4
19=6
20=8
21=10
22=12
23=14
24=16
25=18
26=20
27=22
28=24
29=26
30=28
31=30
32=0
33=2
34=4
35=6
36=8
37=10
38=12
39=14
40=16
41=18
42=20
43=22
44=24
45=26
46=28
47=30
48=32
49=34
50=36
51=38
52=40
53=42
54=44
55=46
56=48
57=50
58=52
59=54
60=56
61=58
62=60
63=62
64=0
65=2
66=4
67=6
68=8
69=10
70=12
71=14
72=16
73=18
74=20
75=22
76=24
77=26
78=28
79=30
80=32
81=34
82=36
83=38
84=40
85=42
86=44
87=46
88=48
89=50
90=52
91=54
92=56
93=58
94=60
95=62
96=64
97=66
98=68
99=70
100=72
101=74
102=76
103=78
104=80
105=82
106=84
107=86
108=88
109=90
110=92
111=94
112=96
113=98
114=100
115=102
116=104
117=106
118=108
119=110
120=112
121=114
122=116
123=118
124=120
125=122
126=124
127=126
128=0
129=2
130=4
131=6
132=8
133=10
134=12
135=14
136=16
137=18
138=20
139=22
140=24
141=26
142=28
143=30
144=32
145=34
146=36
147=38
148=40
149=42
150=44
151=46
152=48
153=50
154=52
155=54
156=56
157=58
158=60
159=62
160=64
161=66
162=68
163=70
164=72
165=74
166=76
167=78
168=80
169=82
170=84
171=86
172=88
173=90
174=92
175=94
176=96
177=98
178=100
179=102
180=104
181=106
182=108
183=110
184=112
185=114
186=116
187=118
188=120
189=122
190=124
191=126
192=128
193=130
194=132
195=134
196=136
197=138
198=140
199=142
После чего получается некая формула арифметической последовательности, вычисление члена которой я представил в виде метода.
private static int GetLoserIndex(int count)
{
int i = 1;
while (i * 2 <= count)
i *= 2;
return (count - i) * 2;
}
Тогда основной метод сводится к такому коду
private static void RemoveEachSecondItem<T>(ICollection<T> collection)
{
int index = GetLoserIndex(collection.Count);
foreach (var a in collection.Where((x, i) => i != index).ToArray())
{
collection.Remove(a);
}
}
Преимущество такого решения в том, что оно будет очень сильно быстрее любого другого на основе перебора.
Может это даже еще можно оптимизировать, но я просто хотел показать идею.