Среди публикаций на Infostart обнаружил следующую (https://infostart.ru/public/612325/). Среди приведенного автором списка задач есть такая:
Требуется найти самую длинную непрерывную цепочку нулей в последовательности нулей и единиц. Входные данные: последовательности нулей и единиц. Выходные данные: число (длина цепочки) Автор предлагает вариант перебора входной строки. Как мне кажется, эта задача может быть решена значительно проще с помощью встроенных функций платформы.
(6) Надежда была изначально на этот метод "//ТЗ.ЗагрузитьКолонку(МассивСтрок, "Строки");", но он не работает пока не создашь пустые строки в ТЗ, поэтому и получилось как получилось. Клиент или сервер условий не было.
(6) Вопрос, как к опытному, есть способ загрузить массив в ТЗ не добавляя пустые строки по количеству элементов массива как при использовании ЗагрузитьКолонку?
(10) Какой-то странный метод от 1С... Если ТЗ наполнена данными метод ЗагрузитьКолонку() подойдет в особых случаях, если ТЗ пустая почему строки не добавить автоматически по количеству элементов массива?
(9) Нет.
А сортировать можно и список значений. В который можно загрузить массив без предварительного добавления элементов списка. И список значений доступен на клиенте.
Синтаксис:
ЗагрузитьЗначения(<МассивЗначений>)
Параметры:
<МассивЗначений> (обязательный)
Тип: Массив.
Массив, содержащий значения для заполнения списка значений.
Описание:
Загружает список значений значениями из переданного массива. При этом все прежние элементы списка удаляются.
(16) Это частный случай. А если задача будет найти не цепочку нулей, а самую длинную цепочку "без единиц"? Тогда будет работать и решение в (5). Оно всегда будет работать (особенно если вынести его в отдельную функцию, и передавать в параметр разделитель).
А твоё - не будет )))
(18) Частный случай к конкретным условиям задачи? Одна и та же функция используется СтрРазделить(), что мешает вынести разделитель из моей? Можно подробнее...
МаксимальнаяДлина = 0;
СтрокаНолейИЕдениц = "0100011100000011001";
МассивСтрок = СтрРазделить(СтрокаНолейИЕдениц, "1");
Для каждого ЗаписьСтрока Из МассивСтрок Цикл
Если МаксимальнаяДлина < СтрДлина(ЗаписьСтрока) Тогда
МаксимальнаяДлина = СтрДлина(ЗаписьСтрока)
КонецЕсли;
КонецЦикла;
Функция ЗадачаДлинаЦепочки(вхСтрока)
счетчик=0;
дСтроки=СтрДлина(вхСтрока);
маска="0";
пока истина цикл
приемник=СтрЗаменить(вхСтрока,маска,"");
если СтрДлина(приемник)=дСтроки тогда
прервать
конецесли;
счетчик=счетчик+1;
маска=маска+"0";
конеццикла;
возврат счетчик;
КонецФункции
(26) Мммм... Тогда мне интересно - в каком случае значение "дСтроки" станет равной значению СтрДлина(Приемник)?
Ведь "дСтроки" у нас - максимальная длина первоначальной вхСтрока, и нигде далее по циклу не изменяется.
А вот длина "приемника" - всегда уменьшается в цикле за счет увеличения "маски".
Таким образом я хочу понять - в каком случае сработает "Прервать"???
Таким образом я хочу понять - в каком случае сработает "Прервать"???
когда после СтрЗаменить ничего не изменится в первоначальной строке.
А в цикле в СтрЗаменить передается нарастающая строка из нулей.
маска=маска+"0";
Как только цепочка нулей превысит существующую цепочку в первоисточнике, то и замены не произойдет. Вот и получится Макс длина такой цепочки (вычисленная на предыдущей итерации).
(39) Долго думал. Логику понял. Попытка работать от обратного. Но это не кошерно.
Сразу вспомнил свою работу в санатории. Там был хороший кореш, который работал одновременно и гастроэнтерологом и проктологом.
Главная его фраза - "не забывать руки мыть".
(33)Как то так. (В первоначальном варианте была ошибка, исправил.)
Функция ЗадачаДлинаЦепочки(вхСтрока)
дСтроки=СтрДлина(вхСтрока);
маска="0";
приемник=СтрЗаменить(вхСтрока,маска,"");
счетчик=дСтроки-СтрДлина(приемник);
если счетчик <2 тогда
возврат счетчик;
конецесли;
для н=1 по счетчик цикл
маска=маска+"0";
конеццикла;
счетчик=счетчик+1;
пока истина цикл
приемник=СтрЗаменить(вхСтрока,маска,"");
если СтрДлина(приемник)<>дСтроки тогда
прервать
конецесли;
счетчик=счетчик-1;
маска=Лев(маска,счетчик);
конеццикла;
возврат счетчик;
КонецФункции
(37) По мне так еще хуже стало... Ты точно программист?
ВхСтрока = "000001111000101010101111110000";
дСтроки=СтрДлина(вхСтрока); // Получаем 30
Приемник=СтрЗаменить(вхСтрока,"0",""); // Получаем "11111111111111"
Счетчик=дСтроки-СтрДлина(приемник); // 30 - 14 = 16
//
для н=1 по счетчик цикл
маска=маска+"0";
конеццикла; // Добавляем к маске 16 нулей, и получаем маску(!!!) в семндацать(!) нулей.
//
пока истина цикл
приемник=СтрЗаменить(вхСтрока,маска,""); // То есть пытаемся найти в строке 17(!!!) нулей. Но их там нет.
если СтрДлина(приемник)<>дСтроки тогда // Никогда не равна. Длина входной строки 30, а приемника 16
прервать
конецесли;
счетчик=счетчик-1; // Счетчик стал 16
маска=Лев(маска,счетчик);
// А приёмник не изменился!!!
конеццикла;
(37) Еще один способ, на случай если отберут СтрРазделить() или СтрЗаменить(). Для любителей пузырьков.
&НаКлиенте
Функция ЗадачаДлинаЦепочкиПоловинноеДеление(ВхСтрока)
ДлинаСтроки = СтрДлина(вхСтрока);
СтрокаИзНулей = СтрЗаменить(ВхСтрока, "1", "");
КоличествоНулей = СтрДлина(СтрокаИзНулей);
Маска = СтрокаИзНулей;
ИсходнаяСтрока = ВхСтрока;
Возврат ПроверитьВхождениеСтроки(Маска, КоличествоНулей)
КонецФункции
&НаКлиенте
Функция ПроверитьВхождениеСтроки(Маска, КоличествоНулей)
КоличествоИтераций = 1 + КоличествоИтераций;
ПозицияНуля = СтрНайти(ИсходнаяСтрока, Маска);
Если ПозицияНуля = 0 Тогда //не нашли, идем влево
КоличествоНулей = Окр(КоличествоНулей / 2);
Маска = Лев(СтрокаИзНулей, КоличествоНулей);
Возврат ПроверитьВхождениеСтроки(Маска, КоличествоНулей);
Иначе //нашли
Если СтрНайти(ИсходнаяСтрока, Маска + "0") = 0 Тогда //проверим что по маске в которой на один "0" больше нет
Возврат КоличествоНулей;
Иначе //нашли по маске в которой на один "0" больше
КоличествоНулей = КоличествоНулей + Окр(КоличествоНулей / 2); //идем вправо
Маска = Лев(СтрокаИзНулей, КоличествоНулей);
Возврат ПроверитьВхождениеСтроки(Маска, КоличествоНулей);
КонецЕсли;
КонецЕсли;
КонецФункции
(35) Ну так-то да ))) Всегда интересны умения программиста обращаться с базовыми конструктами программирования. Потому что сегодня СтрРазделить() или СтрЗаменить() есть в платформе, а завтра его уже нет.
И это даже не про умения. Это про понимание принципов.
(43) В первых релизах С++ был С и пара "++" )))
Но там все это можно было писать, и работало это быстро. Вот пример кода со "стрзаменить" - это постоянный обход массива, т.е. цикл в цикле. Кажется, что платформа делает это быстро, но если сравнить с простым проходом по двоичным данным, то последнее будет делать это за одну итерацию цикла, т.к. никто не знает, будет ли строка заканчиваться единицей. С друго стороны, если у нас длина максимальной последовательности нулей больше, чем остаток строки и мы встретили единицу, то цикл можно заканчивать.
Фреккен Бок напекла на кухне плюшки.
И разложила их в 15 тарелок: в первую тарелку 1 плюшку, в вторую - 2 плюшки,... в 15-ю -15 плюшек.
Карлсон собрался их пи... воровать и таскать из кухни в спальню.
Условие: За один прилет в кухню Карслсон может взять плюшки "с любого количества тарелок", но с каждой выбранной тарелки можно взять только "одинаковое количество за один прилет". (нельзя с одной тарелки взять 2, а с другой -три).
Какие минимальное количество "прилетов" понадобиться Карлсону, чтобы стащить все плюшки?
Объяснить логику, привести конечную формулу.
Задача на программирование. 1С тут ни при чем. Но этот алгоритм вполне себе используется в прикладных задачах бизнеса.
(Если что - то конечную формулу можно выразить встроенными средствами 1С, но это не главное)
(46) ТЫ ЗНАЛ! Но, ты обратным методом (от максимума к минимуму). Я решал наоборот (у нас разный циклы в решении получатся просто). Что не отменяет правильности обоих решений ))
Но вопрос про финальную формулу расчета количества ходов остается открытым.
Сделал сравнение двух алгоритмов. На тестовых данных ДлинаЦепочкиСтрЗаменить работает быстрее чем ДлинаЦепочкиСтрРазделить.
Функция ДлинаЦепочкиСтрРазделить(вхСтрока) экспорт
список=новый СписокЗначений;
список.ЗагрузитьЗначения(СтрРазделить(вхСтрока,"1"));
список.СортироватьПоЗначению(НаправлениеСортировки.Убыв);
счетчик=СтрДлина(список.Получить(0));
возврат (счетчик);
КонецФункции
Функция ДлинаЦепочкиСтрЗаменить(вхСтрока)
дСтроки=СтрДлина(вхСтрока);
маска="0";
приемник=СтрЗаменить(вхСтрока,маска,"");
ЧислоЕдиниц=СтрДлина(приемник) ;
ЧислоНулей =дСтроки-СтрДлина(приемник);
если ЧислоНулей <2 тогда
возврат ЧислоНулей;
конецесли;
//минимальная длина цепочки
счетчик=(ЧислоНулей-ЧислоНулей%(ЧислоЕдиниц+1))/(ЧислоЕдиниц+1);
для н=1 по счетчик-1 цикл
маска=маска+"0";
конеццикла;
если счетчик=0 тогда
счетчик=1;
конецесли;
пока истина цикл
приемник=СтрЗаменить(вхСтрока,маска,"");
если СтрДлина(приемник)=дСтроки тогда
прервать
конецесли;
счетчик=счетчик+1;
маска=маска+"0";
конеццикла;
возврат счетчик-1;
КонецФункции
Функция ТестоваяСтрока(вхДлина)
ГСЧ = Новый ГенераторСлучайныхЧисел(ТекущаяУниверсальнаяДатаВМиллисекундах());
т="";
для н=1 по вхДлина цикл
т=т+ГСЧ.СлучайноеЧисло(0,1);
конеццикла;
возврат т;
КонецФункции
Функция ТестированиеАлгоритмов(вхДлина) экспорт
перем счетчик;
т=ТестоваяСтрока(вхДлина);
для каждого имя из СтрРазделить("ДлинаЦепочкиСтрЗаменить(т),ДлинаЦепочкиСтрРазделить(т)",",") цикл
старт=ТекущаяУниверсальнаяДатаВМиллисекундах();
Выполнить("счетчик="+имя);
сообщить("Результат "+счетчик+" "+имя+ " "+(ТекущаяУниверсальнаяДатаВМиллисекундах()-старт));
конеццикла;
КонецФункции
(58) потому что ГСЧ более менее равномерно получается распределить 0 и 1. Больших цепочек не получится.
Ну и СтрРазделить(вхСтрока,"1"). Тут более уместно не выбирать пустые значения.
И основное, ДлинаЦепочкиСтрЗаменить(т) будет не всегда выигрывать. Я привел пример.
Просто слабое место в ДлинаЦепочкиСтрРазделить(т) это использование списка значений и его сортировка. Когда цепочка будет большой, а количество таких цепочек будет немного, то ДлинаЦепочкиСтрЗаменить(т) существенно проигрывает.
Исправил. Для предложенной тестовой строки картина поменялась. Для строки через генератор случайных чисел нет, как правильно замечено, из-за равномерности нулей и единиц.
(64)
Все верно, через Вычислить получается быстрее.
Длина строки 500 000 Результат 20 ДлинаЦепочкиСтрЗаменить(т) 192
Длина строки 500 000 Результат 20 ДлинаЦепочкиСтрРазделить(т) 352
Длина строки 500 000 Результат 20 ДлинаЦепочкиВычислить(т) 158
Цель обсуждения была найти новые алгоритмы, что и было достигнуто.
(66) и вишенка на тортик... перебор в цикле быстрее всего :)
Результат для тех же 500 000 :
Функция ДлинаЦепочкиСтрРазделитьПеребор(вхСтрока)
Длина = 0;
ВхМассив = СтрРазделить(вхСтрока,"1", Ложь);
Для Каждого СтрМассива Из ВхМассив Цикл
Длина = Макс(Длина, СтрДлина(СтрМассива));
КонецЦикла;
Возврат Длина;
КонецФункции
Интересно, а что будет с быстродействием при тупой проверке каждого символа в строке, вообще без ее преобразования? То есть:
Функция ДлинаЦепочкиСтр(вхСтрока)
Длина = 0;
МаксДлина = 0;
Сч = СтрДлина(вхСтрока);
Пока Сч > 0 Цикл
Если Сред(вхСтрока,Сч,1) = "0" Тогда
Длина = Длина + 1;
Иначе
МаксДлина = Макс(МаксДлина, Длина);
Длина = 0;
КонецЕсли;
Сч = Сч - 1;
КонецЦикла;
Возврат МаксДлина;
КонецФункции
Показать
Проверьте, если не трудно?
P.S. Маленький плюс данного алгоритма - "ненужный" символ необязательно должен быть "1", он может быть любым, даже набором самых разных символов - будет найдена длина последовательности именно нулей.
весь код тестов тут приведен. можете сами проверить.
Результат 19 ДлинаЦепочкиСтрЗаменить(т) 237
Результат 19 ДлинаЦепочкиСтрРазделить(т) 308
Результат 19 ДлинаЦепочкиВычислить(т) 177
Результат 19 ДлинаЦепочкиСтрРазделитьПеребор(т) 140
Результат 19 ДлинаЦепочкиСтр(т) 794
В данном случае усложнение кода дает дихотомия, но она же дает и эффективность (скорость выполнения).
Для сравнения можно упростить алгоритм: создать строку из одних нулей, по длине равную обрабатываемой, и в цикле "откусывать" от нее по одному нолику до тех пор, пока СтрокаИзНулей не будет найдена в вхСтрока - элементарно, одна строка кода внутри цикла.
Вот только выполняться такой код может гораздо дольше предложенного в (49) - так, при длине исходной строки в 1024 символа сложный цикл с половинным делением выполнится максимум 10 раз, а "простой" - до 1023.