Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум «Всё о Паскале» _ Задачи _ Ладьи

Автор: nastya_ab 16.06.2007 11:11

Помогите пожалуйста!

Ладьи.
Требуется найти такую расстановку 8 ладей на шахматной доске, при которой они не будут угрожать друг другу. Первые три фигуры расставляет пользователь.

Автор: Lapp 16.06.2007 16:19

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

  for i:=4 to 8 do begin
repeat l:=Chr(Random(8)+65) until not (l in Letters);
repeat n:=Random(8)+1 until not (n in Numbers);
WriteLn(l,' ',n);
Include(Letters,l);
Include(Numbers,n);
end;

Автор: nasta_ab 17.06.2007 23:49

Цитата(Lapp @ 16.06.2007 12:19) *

Введи два множества: для букв и для цифр. Затем выбирай расположение так, чтобы ни буква, ни цифра не была в соответствующем множестве (случайный перебор). Найденные позиции добавляй к множествам.
То есть примерно вот так (опускаю тут ручной ввод позиций первых трех ладей) :
  for i:=4 to 8 do begin
repeat l:=Chr(Random(8)+65) until not (l in Letters);
repeat n:=Random(8)+1 until not (n in Numbers);
WriteLn(l,' ',n);
Include(Letters,l);
Include(Numbers,n);
end;



Спасибо.

Автор: klem4 18.06.2007 10:36

На сомом деле не лучший вариант, и по идее чисто теоретически возможен вариант когда решение никогда не будет найдено, считай фактически зацикливание, маловероятно конечно но всеже ...

Автор: Michael_Rybak 18.06.2007 11:55

А по-моему вариант хороший smile.gif Вероятность того, что оно зациклится хотя бы на пять секунд, я думаю, меньше вероятности того, что за эти пять секунд вырубится электричество в комп. классе во время проверки smile.gif А раз просят не какое-то определенное решение, то прикольно, чтоб выдавало рандомное.

Автор: Lapp 19.06.2007 16:21

А мне кажется, что klem4 прав. По сути, я ждал этого вопроса. Клем только забыл главный аргумент - масштабируемость. Ведь никто не сказал, что доска не может быть, скажем, 1000х1000 или еще больше.. И тогда эффективность алгоритма резко упадет..

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

const
b=8; {размер доски}
m=3; {начальное число ладей}

var
l:char;
n,k:byte;
i:integer;
Letters:set of char;
Numbers:set of byte;

begin
Letters:=[];
Numbers:=[];
for i:=1 to m do begin
Write('Введите положение (буква и цифра через пробел) ладьи ',i,': ');
ReadLn(l,n);
Include(Letters,UpCase(l));
Include(Numbers,n);
end;
WriteLn('Остальные ладьи можно расставить так:');
for i:=m+1 to b do begin
k:=Random(b-i+1)+1;
l:='@';
repeat
Inc(l);
while l in Letters do Inc(l);
Dec(k)
until k=0;
Include(Letters,l);
k:=Random(b-i+1)+1;
n:=0;
repeat
Inc(n);
while n in Numbers do Inc(n);
Dec(k)
until k=0;
Include(Numbers,n);
WriteLn(l,' ',n)
end;
ReadLn
end.

Алгоритм, вроде, работает (хотя я уже практически сплю, могут быть ошибки.. smile.gif), но мне почему-то кажется, что он неоптимальный. Кто-нить может быстрее? smile.gif
При существенном увеличении размера доски буквы можно заменить на цифры.

Автор: Michael_Rybak 20.06.2007 15:01

Во-первых, 1000х1000 не получится из-за set.

А быстрее можно, используя специальную структуру данных, которая позволит находить нужную позицию за логарифмическое время. Т.е. будет n log n

Могу дать ссылку где почитать, если интересно smile.gif

Автор: Lapp 20.06.2007 15:28

Цитата(Michael_Rybak @ 20.06.2007 12:01) *

Могу дать ссылку где почитать, если интересно smile.gif

Фи, ссылка...
А мозги на что? smile.gif))

Да, с множествами я лопухнулся, понял наутро smile.gif. 255 - и отвали..
Значит, надо иначе

Автор: volvo 20.06.2007 15:35

Цитата
255 - и отвали..
Или реализуй BigSet (почему я и пользуюсь всегда Include/Exclude вместо +/-) и работай хоть до 10000 ...

Автор: Michael_Rybak 20.06.2007 19:09

Цитата
Фи, ссылка...


Договорились - давайте придумывать smile.gif

Автор: Lapp 23.06.2007 6:34

Цитата(Michael_Rybak @ 20.06.2007 16:09) *

Договорились - давайте придумывать smile.gif

Я некоторое время чувствовал себя некомфортно, как бы с долгом smile.gif. Сегодня нашел минутку подумать над этой задачей. Вот результат.. smile.gif
Я привожу код, который заполняет массив [1..n] неповторяющимися случайными числами от 1 до n. Код не использует множеств, так что размер массива ограничен выбранным типом переменных (в ТР - по сути, размером блока). Код должен работать, мне кажется, быстрее предыдущего моего варианта.
Принцип такой:
- заполняем массив (RestNum) номерами по порядку (это те номера, которые остались);
- в цикле выдергиваем из этого массива по одному элементу случайным образом и кладем в массив Num, заполняя его по порядку;
- образовавщуюся в RestNum от изымания элемента дыру заполняем, сдвигая хвост массива на один элемент влево, начиная с номера сразу за дырой;
- размер RestNum при этом как бы эффективно уменьшается на 1 (переменная m), что мы учитываем, уменьшая диапазон случайных чисел.
Вот сам код. Его можно оформить в виде процедуры при желании smile.gif. Если нужно только выводить номера на печать или в файл, то массив Num по сути не нужен, можно вдвое сэкономить память.
const
n=20;

type
tRange=array[1..n]of integer;

var
Num,RestNum:tRange;
i,j,k,m:integer;

begin
for i:=1 to n do RestNum[i]:=i;
m:=n;
for i:=1 to n do begin
k:=Random(m)+1;
Num[i]:=RestNum[k];
if k<m then Move(RestNum[k+1],RestNum[k],(m-k)*SizeOf(Integer));
Dec(m)
end;
for i:=1 to n do Write(Num[i]:3);
ReadLn
end.

Автор: Michael_Rybak 23.06.2007 17:04

Lapp, +1. А я знаете как решил ее относительно недавно? За n log n. Берем группу, идем по ней, и разбиваем на две случайным образом: каждый элемент с равной вероятностью оказывается в первой или второй. Потом рекурсивно перемешиваем подгруппы, и сливаем вместе smile.gif

Кстати, std::random_shuffle в C++ работает именно так, как вы описали.

А что касается специальных структур данных, о которых я говорил - с их помощью можно работать с массивом, сохраняя порядок (не перемещая хвост в дырку).