Функция для получения возможных перестановок или комбинаторика для 1С-нега

05.06.15

Разработка - Универсальные функции

Функция, возвращающая массив всех возможных перестановок произвольного множества, состоящих из заданного количества элементов. С повторениями элементов или без.

Понадобилось (не спрашивайте зачем) найти и проанализировать все возможные комбинации элементов массива произвольной длины. Задача хрестоматийная. Так как число возможных элементов и длина комбинации на этапе постановки задачи не определены, напрашивается рекурсивное решение. И что же я нахожу по теме? Ничего для 1С, но множество всяких разных реализаций на Сях, Дельфях и даже VBA.

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

Погордился собой минут 20 и решил поделиться с сообществом. Может, кому-нибудь пригодится, и будет моей карме лишний лайк:)

Сама функция:

//мЭлементов 		- массив произвольных элементов, образующих комбинации. Произвольный.
//ДлинаПерестановки	- количество элементов в комбинации. Целое.
//БезПовторов		- признак необходимости получать в результате комбинации в которых один и тот же элемент мЭлементов встречался бы не чаще 1 раза. Булево. По умолчанию - Ложь.

Функция Перестановки(мЭлементов, ДлинаПерестановки, БезПовторов = Ложь, Основание = Неопределено, мРезультата = Неопределено, ТекУровень = 0) Экспорт
	
	Если Основание = Неопределено Тогда Основание = Новый Массив КонецЕсли;
	Если мРезультата = Неопределено Тогда мРезультата = Новый Массив КонецЕсли;
	
	Если ТекУровень < ДлинаПерестановки - 1 Тогда		
		Для Каждого Элемент Из мЭлементов Цикл	
			Если БезПовторов И НЕ Основание.Найти(Элемент) = Неопределено Тогда
			Иначе
				Основание.Добавить(Элемент);
				мРезультата = Перестановки(мЭлементов, ДлинаПерестановки, БезПовторов, Основание, мРезультата, ТекУровень + 1);
				Основание.Удалить(Основание.Количество() - 1);
			КонецЕсли;
		КонецЦикла;
	Иначе
		Для Каждого Элемент Из мЭлементов Цикл
			Если БезПовторов И НЕ Основание.Найти(Элемент) = Неопределено Тогда
			Иначе
				Основание.Добавить(Элемент);
				мРезультата.Добавить(Новый ФиксированныйМассив(Основание));
				Основание.Удалить(Основание.Количество()-1);
			КонецЕсли;
		КонецЦикла;	
	КонецЕсли;	
	Возврат мРезультата;	
КонецФункции

По аргументам все, надеюсь, понятно из описания. По результатам: на выходе получаем массив из фиксированных массивов. Фиксированный массив - возможная комбинация, а количество элементов результирующего массива и есть количество найденных (возможных) комбинаций. Каждый фиксированный массив состоит из ДлинаПерестановки элементов массива мЭлементов - то есть какой-то набор элементов первоначального множества значений. Или одна из возможных комбинаций.

Очевидная вещь, но все таки предупрежу. Если ДлинаПерестановки в аргументах будет больше числа элементов в массиве мЭлементов, а условие уникальности при этом будет Истина, в результате Вы получите замечательный, но пустой массив результатов. Почему? Да потому, что нельзя собрать из Х возможных  элементов комбинацию длиной в Y, не повторяясь, если X<Y. Допустим, в 11 разрядном десятичном числе как минимум одна из цифр должна повториться дважды. Вот как-то так получается:)

Всё!

P.S. Предвидя возмущения широкой общественности по поводу "нафига нам честным, умным, скромным и обычным 1С-негам это нужно" вспомнил одну  публикацию по теме, как помочь бухгалтеру найти, какие же из возможных чисел образуют в сумме ошибку. Вот Вам и готовая сфера применения функции. И при этом используется настоящий, выдержанный и марочный 1С, а не Java:)

Вступайте в нашу телеграмм-группу Инфостарт

комбинация комбинации перестановки сочетания комбинаторный алгоритм комбинаторика

Вы можете заказать платную адаптацию этой статьи под ваши задачи на «Бирже заказов».

  • 0% комиссии — оплата напрямую исполнителю;
  • Исполнители любого масштаба — от отдельных специалистов до команд под проект;
  • Прямой обмен контактами между заказчиком и исполнителем;
  • Безопасная сделка — при необходимости;
  • Рейтинги, кейсы и прозрачная система откликов.

См. также

Загрузка и выгрузка в Excel Универсальные функции Программист 1С:Предприятие 8 Россия Бесплатно (free)

Описанный ниже подход позволяет в три шага заполнять формулы в Excel файлы, вне зависимости от ОС сервера (MS Windows Server или Linux). Подход подразумевает отказ от работы с COM-объектом в пользу работы через "объектную модель документа" (DOM).

30.10.2025    4826    Abysswalker    11    

47

Универсальные функции Работа с интерфейсом Программист 1С:Предприятие 8 Бесплатно (free)

Порой необходимо временно отключить расширение 1С, не удаляя его, чтобы не потерять данные. Но в этом случае при каждом запуске всем будет лезть уведомление о неактивном расширении, хотя очевидно, это техническая информация, которой не стоит лишний раз пугать пользователей.

14.05.2025    9027    DeerCven    15    

63

Универсальные функции Программист 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

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

21.05.2024    57917    dimanich70    85    

175

Универсальные функции Программист 1С:Предприятие 8 1C:Бухгалтерия Абонемент ($m)

Задача: вставить картинку из буфера обмена на форму средствами платформы 1С.

1 стартмани

18.03.2024    8138    7    John_d    13    

59

Универсальные функции Программист Стажер 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

Пришлось помучиться с GUID-ами немного, решил поделиться опытом, мало ли кому пригодится.

12.02.2024    72664    atdonya    31    

73

Универсальные функции Программист 1С:Предприятие 8 Бесплатно (free)

На заключительных этапах, когда идет отладка или доработка интерфейса, необходимо много раз переоткрыть внешний объект. Вот один из способов автоматизации этого.

30.11.2023    10112    ke.92@mail.ru    17    

68
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. Zeskord 08.06.15 12:57 Сейчас в теме
Посмотрите в БСП функцию
УправлениеДоступомСлужебный.ВсеКомбинацииВидовДоступа

Там эта задача по-другому решена.
2. dusha0020 1125 08.06.15 15:28 Сейчас в теме
(1) Zeskord, К сожалению у меня в БСП такой функции нет.
3. rayastar 1579 08.06.15 19:19 Сейчас в теме
вот тоже слегка смежная работа
http://infostart.ru/public/240261/
5. dusha0020 1125 09.06.15 08:26 Сейчас в теме
(3) rayastar, А почему слегка? Не качал Вашу работу, но подозреваю что суть алгоритма генерации комбинаций все та же - рекурсивный перебор элементов множества. Мне попадался очень интересный лексографический алгоритм, который в теории (имхо) должен обеспечивать большую скорость генерации, но так как генерация комбинаций была лишь попутной для меня задачей разбираться не стал - ограничился рекурсией и убрал все лишнее. Массив исходных данных также в моем случае был вынужденной мерой, так как перестановки нужны были не для чисел или символов, а для вершин графа, которые были представлены то структурами то строками ТЗ. В итоге функция получилась довольно компактной и предельно универсальной.
А теперь мой вопрос:) Применение данной функции для оптимизации отображения графа на плоскости дало результат, но меня беспокоит быстродействие. Дело в том, что у меня граф рисуется довольно брутально - генерируются все возможные расположения вершин в узлах сетки и перебираются с пересчетом количества пересечений ребер графа. Если пересечений = 0, поиск прерывается, но в сложных графах такого практически не бывает и уже при не очень большом количестве вершин (15-20) пересчет длится 2-3 минуты на сервере. Больше половины этого времени занимает генерация комбинаций. Может Ваша реализация алгоритма быстрее моей? Как считаете?
VyacheslavShilov; +1 Ответить
4. vova-1c 154 08.06.15 22:07 Сейчас в теме
На мой взгляд очень даже хорошо.
6. ichhh 1 25.04.17 16:07 Сейчас в теме
Дружище, не представляешь как я тебе благодарен! Огромное спасибо)) этот код мне десяток, другой часов сэкономил т.к. знаю про себя что алгоритмист я так себе.
ZDmitry83; +1 Ответить
7. user726666 02.08.18 10:25 Сейчас в теме
Реально выручил, спасибо!
8. dusha0020 1125 03.08.18 09:59 Сейчас в теме
9. user848614 09.08.19 12:28 Сейчас в теме
Спасибо, помогла функция)
10. tvssspb 22.01.20 14:50 Сейчас в теме
мне подошло на 100%
особое спасибо за параметры, благодаря чему всего 3 мин на внедрение! ))
11. druv 192 29.05.20 15:31 Сейчас в теме
Спасибо. Время сэкономил.
12. IVANTHESPACEBIKER 11.01.21 14:54 Сейчас в теме
Чуть модифицировал, чтобы была возможность получить комбинации без учета порядка ("1,2" и "2,1" не повторятся). М.б. кому сгодится.

//мЭлементов         - массив произвольных элементов, образующих комбинации. Произвольный.
//ДлинаПерестановки    - количество элементов в комбинации. Целое.
//БезПовторов        - признак необходимости получать в результате комбинации в которых один и тот же элемент мЭлементов встречался бы не чаще 1 раза. Булево. По умолчанию - Ложь.
Функция Перестановки(мЭлементов, ДлинаПерестановки, БезПовторов = Ложь, БезУчетаПорядка = Ложь, Основание = Неопределено, мРезультата = Неопределено, ТекУровень = 0) Экспорт
    
    Если Основание = Неопределено Тогда Основание = Новый Массив КонецЕсли;
    Если мРезультата = Неопределено Тогда мРезультата = Новый Массив КонецЕсли;
    
    Если ТекУровень < ДлинаПерестановки - 1 Тогда        
        Для Каждого Элемент Из мЭлементов Цикл    
            Если (БезПовторов И НЕ Основание.Найти(Элемент) = Неопределено) Тогда
            Иначе
                Основание.Добавить(Элемент);
                мРезультата = Перестановки(мЭлементов, ДлинаПерестановки, БезПовторов, БезУчетаПорядка, Основание, мРезультата, ТекУровень + 1);
                Основание.Удалить(Основание.Количество() - 1);
            КонецЕсли;
        КонецЦикла;
    Иначе
        Для Каждого Элемент Из мЭлементов Цикл
            Если (БезПовторов И НЕ Основание.Найти(Элемент) = Неопределено) Тогда
			Иначе
				Основание.Добавить(Элемент);
				Результат = Новый ФиксированныйМассив(Основание);
				Если Не БезУчетаПорядка Или (БезУчетаПорядка И Не ЕстьЭлементБезУчетаПорядка(Результат, мРезультата)) Тогда 
					мРезультата.Добавить(Новый ФиксированныйМассив(Основание));
				КонецЕсли;
                Основание.Удалить(Основание.Количество()-1);
            КонецЕсли;
        КонецЦикла;    
    КонецЕсли;    
    Возврат мРезультата;    
КонецФункции	

Функция ЕстьЭлементБезУчетаПорядка(Результат, мРезультата)

	Для Каждого ТекРезультат Из мРезультата Цикл
		Если ТекРезультат.Количество() <> Результат.Количество() Тогда 
			Продолжить;
		КонецЕсли;
		
		флПолноеСовпадение = Истина;
		Для Каждого Элемент Из Результат Цикл
			Если ТекРезультат.Найти(Элемент) = Неопределено Тогда 
				флПолноеСовпадение = Ложь;
				Прервать;
			КонецЕсли;			
		КонецЦикла;
		
		Если флПолноеСовпадение Тогда 
			Возврат Истина;
		КонецЕсли;
		
	КонецЦикла;
	
	Возврат Ложь;

КонецФункции // ЕстьЭлементБезУчетаПорядка()

Показать
VyacheslavShilov; RidelAV; maikl007; zannv; Gisborn; dusha0020; +6 Ответить
13. binex 280 10.03.21 11:11 Сейчас в теме
Рефакторинг.


Функция Перестановки(мЭлементов, ДлинаПерестановки, 
						БезПовторов = Ложь, 
						Основание = Неопределено, 
						мРезультата = Неопределено, 
						ТекУровень = 0) Экспорт
    
    Если Основание = Неопределено Тогда Основание = Новый Массив КонецЕсли;
    Если мРезультата = Неопределено Тогда мРезультата = Новый Массив КонецЕсли;
    
	Для Каждого Элемент Из мЭлементов Цикл
		
		Если БезПовторов И Основание.Найти(Элемент) <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		
        Основание.Добавить(Элемент);
		
		Если ТекУровень < ДлинаПерестановки - 1 Тогда
        	мРезультата = Перестановки(мЭлементов, ДлинаПерестановки, БезПовторов, Основание, мРезультата, ТекУровень + 1);
		Иначе	
        	мРезультата.Добавить(Новый ФиксированныйМассив(Основание));
		КонецЕсли;
		
		Основание.Удалить(Основание.Количество() - 1);
		
    КонецЦикла;
	
    Возврат мРезультата;    
	
КонецФункции
Показать
sinichenko_alex; dctvghbdtn; dusha0020; +3 Ответить
14. Al777 22.03.21 00:04 Сейчас в теме
Большое спасибо за такую функцию! Чуть-чуть "допилил" для своих потребностей и вуаля... Получил то, что нужно. Время сэкономил нереально. Когда-то писал такую функцию, правда, ещё на 7.7, работала не ахти, как быстро.
15. cwant 5 24.02.22 22:19 Сейчас в теме
16. sinichenko_alex 220 16.09.22 15:28 Сейчас в теме
Рефакторинг

Процедура КакПользоваться()
	
	Основной = Новый Массив;
	Для Индекс = 0 По 6 Цикл
		Основной.Добавить(Индекс);
	КонецЦикла;
	
	сткПараметры = Новый Структура;
	сткПараметры.Вставить("мЭлементов", Основной);
	сткПараметры.Вставить("ДлинаПерестановки", Основной.Количество());
	сткПараметры.Вставить("БезПовторов", Истина);
	сткПараметры.Вставить("Основание", Новый Массив);
	сткПараметры.Вставить("мРезультата", Новый Массив);
	
	Рез = Перестановки(сткПараметры);
		
КонецПроцедуры

Функция Перестановки(ПараметрыПерестановки, ТекУровень = 0) Экспорт
	
    Для Каждого Элемент Из ПараметрыПерестановки.мЭлементов Цикл
        
        Если ПараметрыПерестановки.БезПовторов И ПараметрыПерестановки.Основание.Найти(Элемент) <> Неопределено Тогда
            Продолжить;
        КонецЕсли;
        
        ПараметрыПерестановки.Основание.Добавить(Элемент);
        
        Если ТекУровень < ПараметрыПерестановки.ДлинаПерестановки - 1 Тогда
            ПараметрыПерестановки.мРезультата = Перестановки(ПараметрыПерестановки, ТекУровень + 1);
        Иначе    
            ПараметрыПерестановки.мРезультата.Добавить(Новый ФиксированныйМассив(ПараметрыПерестановки.Основание));
        КонецЕсли;
        
        ПараметрыПерестановки.Основание.Удалить(ПараметрыПерестановки.Основание.Количество() - 1);
        
    КонецЦикла;
    
    Возврат ПараметрыПерестановки.мРезультата;    
    
КонецФункции
Показать
RidelAV; dusha0020; +2 Ответить
17. user1374747 287 28.03.24 14:57 Сейчас в теме
Однако.

Столкнулся с подобной задачей для поиска по произвольной комбинации слов контекста.
Вроде простая идея, а затруднился. Закопался. Старею, наверное. Забыл комбинаторику.

Решил поискать, нашел здесь.

Спасибо.

Однозначно, плюс.
Для отправки сообщения требуется регистрация/авторизация