Получение набора параметров набора в Java

Силовым набором {1, 2, 3} является:

{{}, {2}, {3}, {2, 3}, {1, 2}, {1, 3}, {1, 2, 3}, {1}}

Предположим, у меня есть Set in Java:

 Set mySet = new HashSet(); mySet.add(1); mySet.add(2); mySet.add(3); Set<Set> powerSet = getPowerset(mySet); 

Как написать функцию getPowerset с наилучшим порядком сложности? (Я думаю, что это может быть O (2 ^ n).)

Да, это действительно O(2^n) , так как вам нужно сгенерировать, ну, возможные комбинации. Вот работающая реализация с использованием дженериков и наборов:

 public static  Set> powerSet(Set originalSet) { Set> sets = new HashSet>(); if (originalSet.isEmpty()) { sets.add(new HashSet()); return sets; } List list = new ArrayList(originalSet); T head = list.get(0); Set rest = new HashSet(list.subList(1, list.size())); for (Set set : powerSet(rest)) { Set newSet = new HashSet(); newSet.add(head); newSet.addAll(set); sets.add(newSet); sets.add(set); } return sets; } 

И тест, учитывая ваш пример ввода:

  Set mySet = new HashSet(); mySet.add(1); mySet.add(2); mySet.add(3); for (Set s : SetUtils.powerSet(mySet)) { System.out.println(s); } 

На самом деле, я написал код, который делает то, о чем вы просите в O (1). Вопрос заключается в том, что вы планируете делать с помощью Set next. Если вы просто назовёте size() на нем, это O (1), но если вы собираетесь повторять его, это, очевидно, O(2^n) .

contains() будет O(n) и т. д.

Вам это действительно нужно?

РЕДАКТИРОВАТЬ:

Этот код теперь доступен в Guava , который Sets.powerSet(set) через метод Sets.powerSet(set) .

Вот решение, в котором я использую генератор, преимущество в том, что весь набор мощности никогда не сохраняется сразу … Таким образом, вы можете перебирать его один за другим, не требуя его сохранения в памяти. Я бы хотел подумать, что это лучший вариант … Обратите внимание, что сложность такая же, O (2 ^ n), но требования к памяти уменьшаются (при условии, что сборщик мусора ведет себя!;))

 /** * */ package org.mechaevil.util.Algorithms; import java.util.BitSet; import java.util.Iterator; import java.util.Set; import java.util.TreeSet; /** * @author st0le * */ public class PowerSet implements Iterator>,Iterable>{ private E[] arr = null; private BitSet bset = null; @SuppressWarnings("unchecked") public PowerSet(Set set) { arr = (E[])set.toArray(); bset = new BitSet(arr.length + 1); } @Override public boolean hasNext() { return !bset.get(arr.length); } @Override public Set next() { Set returnSet = new TreeSet(); for(int i = 0; i < arr.length; i++) { if(bset.get(i)) returnSet.add(arr[i]); } //increment bset for(int i = 0; i < bset.size(); i++) { if(!bset.get(i)) { bset.set(i); break; }else bset.clear(i); } return returnSet; } @Override public void remove() { throw new UnsupportedOperationException("Not Supported!"); } @Override public Iterator> iterator() { return this; } } 

Чтобы вызвать его, используйте этот шаблон:

  Set set = new TreeSet (); for(int i = 0; i < 5; i++) set.add((char) (i + 'A')); PowerSet pset = new PowerSet(set); for(Set s:pset) { System.out.println(s); } 

Это из моей библиотеки Project Euler … 🙂

Если n <63, что является разумным предположением, так как у вас не хватит памяти (если только не используется реализация итератора), пытаясь построить набор настроек, это более краткий способ сделать это. Двоичные операции быстрее, чем Math.pow() и массивы для масок, но почему-то пользователи Java боятся их …

 List list = new ArrayList(originalSet); int n = list.size(); Set> powerSet = new HashSet>(); for( long i = 0; i < (1 << n); i++) { Set element = new HashSet(); for( int j = 0; j < n; j++ ) if( (i >> j) % 2 == 1 ) element.add(list.get(j)); powerSet.add(element); } return powerSet; 

Вот учебник, описывающий, что именно вы хотите, включая код. Вы правы в том, что сложность O (2 ^ n).

Я придумал другое решение, основанное на идеях @Harry He. Наверное, не самый элегантный, но вот он идет, как я понимаю:

Возьмем classический простой пример PowerSet из SP (S) = {{1}, {2}, {3}}. Мы знаем, что формула для получения числа подмножеств равна 2 ^ n (7 + пустое множество). Для этого примера 2 ^ 3 = 8 подмножеств.

Чтобы найти каждое подмножество, нам нужно преобразовать 0-7 десятичных чисел в двоичное представление, показанное в таблице преобразования ниже:

ConversionTable

Если мы пересекаем таблицу по строкам, каждая строка будет иметь подмножество, а значения каждого подмножества будут поступать из разрешенных бит.

Каждый столбец в разделе «Значение бина» соответствует позиции индекса в исходном наборе ввода.

Вот мой код:

 public class PowerSet { /** * @param args */ public static void main(String[] args) { PowerSet ps = new PowerSet(); Set set = new HashSet(); set.add(1); set.add(2); set.add(3); for (Set s : ps.powerSet(set)) { System.out.println(s); } } public Set> powerSet(Set originalSet) { // Original set size eg 3 int size = originalSet.size(); // Number of subsets 2^n, eg 2^3 = 8 int numberOfSubSets = (int) Math.pow(2, size); Set> sets = new HashSet>(); ArrayList originalList = new ArrayList(originalSet); for (int i = 0; i < numberOfSubSets; i++) { // Get binary representation of this index eg 010 = 2 for n = 3 String bin = getPaddedBinString(i, size); //Get sub-set Set set = getSet(bin, originalList)); sets.add(set); } return sets; } //Gets a sub-set based on the binary representation. Eg for 010 where n = 3 it will bring a new Set with value 2 private Set getSet(String bin, List origValues){ Set result = new HashSet(); for(int i = bin.length()-1; i >= 0; i--){ //Only get sub-sets where bool flag is on if(bin.charAt(i) == '1'){ int val = origValues.get(i); result.add(val); } } return result; } //Converts an int to Bin and adds left padding to zero's based on size private String getPaddedBinString(int i, int size) { String bin = Integer.toBinaryString(i); bin = String.format("%0" + size + "d", Integer.parseInt(bin)); return bin; } } 

Если вы используете коллекцию Eclipse (ранее GS Collections ), вы можете использовать метод powerSet() для всех SetIterables.

 MutableSet set = UnifiedSet.newSetWith(1, 2, 3); System.out.println("powerSet = " + set.powerSet()); // prints: powerSet = [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]] 

Примечание. Я являюсь коммиттером для коллекций Eclipse.

Я искал решение, которое было не таким огромным, как те, которые были размещены здесь. Это нацелено на Java 7, поэтому для версий 5 и 6 потребуется несколько паст.

 Set> powerSetofNodes(Set orig) { Set> powerSet = new HashSet<>(), runSet = new HashSet<>(), thisSet = new HashSet<>(); while (powerSet.size() < (Math.pow(2, orig.size())-1)) { if (powerSet.isEmpty()) { for (Object o : orig) { Set s = new TreeSet<>(); s.add(o); runSet.add(s); powerSet.add(s); } continue; } for (Object o : orig) { for (Set s : runSet) { Set s2 = new TreeSet<>(); s2.addAll(s); s2.add(o); powerSet.add(s2); thisSet.add(s2); } } runSet.clear(); runSet.addAll(thisSet); thisSet.clear(); } powerSet.add(new TreeSet()); return powerSet; 

Вот пример кода для тестирования:

 Set hs = new HashSet<>(); hs.add(1); hs.add(2); hs.add(3); hs.add(4); for(Set s : powerSetofNodes(hs)) { System.out.println(Arrays.toString(s.toArray())); } 

Следующее решение заимствовано из моей книги « Кодирование интервью: вопросы, анализ и решения »:

Выбираются некоторые целые числа в массиве, которые составляют комбинацию. Используется набор бит, где каждый бит обозначает целое число в массиве. Если для комбинации выбран i-й символ, i-й бит равен 1; в противном случае это 0. Например, три бита используются для комбинаций массива [1, 2, 3]. Если первые два целых числа 1 и 2 выбраны для составления комбинации [1, 2], соответствующие биты являются {1, 1, 0}. Аналогично, биты, соответствующие другой комбинации [1, 3], являются {1, 0, 1}. Мы можем получить все комбинации массива с длиной n, если мы можем получить все возможные комбинации n бит.

Число состоит из набора бит. Все возможные комбинации n бит соответствуют числам от 1 до 2 ^ n -1. Следовательно, каждое число в диапазоне от 1 до 2 ^ n -1 соответствует комбинации массива с длиной n . Например, число 6 состоит из битов {1, 1, 0}, поэтому первый и второй символы выбираются в массиве [1, 2, 3] для генерации комбинации [1, 2]. Аналогично, число 5 с битами {1, 0, 1} соответствует комбинации [1, 3].

Код Java для реализации этого решения выглядит следующим образом:

 public static ArrayList> powerSet(int[] numbers) { ArrayList> combinations = new ArrayList>(); BitSet bits = new BitSet(numbers.length); do{ combinations.add(getCombination(numbers, bits)); }while(increment(bits, numbers.length)); return combinations; } private static boolean increment(BitSet bits, int length) { int index = length - 1; while(index >= 0 && bits.get(index)) { bits.clear(index); --index; } if(index < 0) return false; bits.set(index); return true; } private static ArrayList getCombination(int[] numbers, BitSet bits){ ArrayList combination = new ArrayList(); for(int i = 0; i < numbers.length; ++i) { if(bits.get(i)) combination.add(numbers[i]); } return combination; } 

Приращение метода увеличивает число, представленное в наборе бит. Алгоритм очищает 1 бит от самого правого до тех пор, пока не будет найден 0 бит. Затем он устанавливает самый правый бит 0 в 1. Например, чтобы увеличить число 5 с битами {1, 0, 1}, он очищает 1 бит с правой стороны и устанавливает крайний правый бит 0 в 1. Биты становятся {1, 1, 0} для числа 6, что является результатом увеличения 5 на 1.

Некоторые из вышеперечисленных решений страдают, когда размер набора является большим, потому что они создают много мусора объекта, который нужно собрать, и требуют копирования данных. Как мы можем избежать этого? Мы можем воспользоваться тем фактом, что мы знаем, насколько большой размер набора результатов будет (2 ^ n), предопределить массив, который большой, и просто добавить к нему, никогда не копируя.

Ускорение быстро растет с n. Я сравнил его с решением Жоау Силвы выше. На моей машине (все приближенные измерения) n = 13 в 5 раз быстрее, n = 14 – 7x, n = 15 – 12x, n = 16 – 25x, n = 17 – 75x, n = 18 – 140x. Так что создание / сбор и копирование мусора доминирует в том, что, похоже, похоже на аналогичные решения для больших O.

Предварительное выделение массива в начале кажется победой по сравнению с тем, что позволяет динамически расти. При n = 18 динамическое выращивание занимает в два раза больше общего.

 public static  List> powerSet(List originalSet) { // result size will be 2^n, where n=size(originalset) // good to initialize the array size to avoid dynamic growing int resultSize = (int) Math.pow(2, originalSet.size()); // resultPowerSet is what we will return List> resultPowerSet = new ArrayList>(resultSize); // Initialize result with the empty set, which powersets contain by definition resultPowerSet.add(new ArrayList(0)); // for every item in the original list for (T itemFromOriginalSet : originalSet) { // iterate through the existing powerset result // loop through subset and append to the resultPowerset as we go // must remember size at the beginning, before we append new elements int startingResultSize = resultPowerSet.size(); for (int i=0; i oldSubset = resultPowerSet.get(i); // create a new element by adding a new item from the original list List newSubset = new ArrayList(oldSubset); newSubset.add(itemFromOriginalSet); // add this element to the result powerset (past startingResultSize) resultPowerSet.add(newSubset); } } return resultPowerSet; } 

Вот простое итеративное решение O (2 ^ n):

 public static Set> powerSet(List intList){ Set> result = new HashSet(); result.add(new HashSet()); for (Integer i : intList){ Set> temp = new HashSet(); for(Set intSet : result){ intSet = new HashSet(intSet); intSet.add(i); temp.add(intSet); } result.addAll(temp); } return result; } 
 import java.util.Set; import com.google.common.collect.*; Set> sets = Sets.powerSet(ImmutableSet.of(1, 2, 3)); 

Если S – конечное множество с N элементами, то множество степеней S содержит 2 ^ N элементов. Время просто перечислять элементы силового набора равно 2 ^ N, поэтому O(2^N) является нижней границей временной сложности (с нетерпением) построения силового набора.

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

Один из способов без рекурсии состоит в следующем: используйте двоичную маску и сделайте все возможные комбинации.

 public HashSet createPowerSet(Object[] array) { HashSet powerSet=new HashSet(); boolean[] mask= new boolean[array.length]; for(int i=0;i 

Алгоритм:

Input: Set [], set_size 1. Получите размер power set powet_set_size = pow (2, set_size) 2 Loop для счетчика от 0 до pow_set_size (a) Loop для i = 0 для set_size (i) Если i-й бит в счетчике равен set Print i-й элемент из набора для этого подмножества (b) Сепаратор печати для подмножеств, т.е. строки новой строки

 #include  #include  void printPowerSet(char *set, int set_size) { /*set_size of power set of a set with set_size n is (2**n -1)*/ unsigned int pow_set_size = pow(2, set_size); int counter, j; /*Run from counter 000..0 to 111..1*/ for(counter = 0; counter < pow_set_size; counter++) { for(j = 0; j < set_size; j++) { /* Check if jth bit in the counter is set If set then pront jth element from set */ if(counter & (1< 

Это мое рекурсивное решение, которое может получить набор мощности любого набора с использованием Java Generics. Его основная идея – объединить голову входного массива со всеми возможными решениями остальной части массива следующим образом.

 import java.util.LinkedHashSet; import java.util.Set; public class SetUtil { private static Set> combine(T head, Set> set) { Set> all = new LinkedHashSet<>(); for (Set currentSet : set) { Set outputSet = new LinkedHashSet<>(); outputSet.add(head); outputSet.addAll(currentSet); all.add(outputSet); } all.addAll(set); return all; } //Assuming that T[] is an array with no repeated elements ... public static Set> powerSet(T[] input) { if (input.length == 0) { Set >emptySet = new LinkedHashSet<>(); emptySet.add(new LinkedHashSet()); return emptySet; } T head = input[0]; T[] newInputSet = (T[]) new Object[input.length - 1]; for (int i = 1; i < input.length; ++i) { newInputSet[i - 1] = input[i]; } Set> all = combine(head, powerSet(newInputSet)); return all; } public static void main(String[] args) { Set> set = SetUtil.powerSet(new Integer[] {1, 2, 3, 4, 5, 6}); System.out.println(set); } } 

Это приведет к выводу:

 [[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5], [1, 2, 3, 4, 6], [1, 2, 3, 4], [1, 2, 3, 5, 6], [1, 2, 3, 5], [1, 2, 3, 6], [1, 2, 3], [1, 2, 4, 5, 6], [1, 2, 4, 5], [1, 2, 4, 6], [1, 2, 4], [1, 2, 5, 6], [1, 2, 5], [1, 2, 6], [1, 2], [1, 3, 4, 5, 6], [1, 3, 4, 5], [1, 3, 4, 6], [1, 3, 4], [1, 3, 5, 6], [1, 3, 5], [1, 3, 6], [1, 3], [1, 4, 5, 6], [1, 4, 5], [1, 4, 6], [1, 4], [1, 5, 6], [1, 5], [1, 6], [1], [2, 3, 4, 5, 6], [2, 3, 4, 5], [2, 3, 4, 6], [2, 3, 4], [2, 3, 5, 6], [2, 3, 5], [2, 3, 6], [2, 3], [2, 4, 5, 6], [2, 4, 5], [2, 4, 6], [2, 4], [2, 5, 6], [2, 5], [2, 6], [2], [3, 4, 5, 6], [3, 4, 5], [3, 4, 6], [3, 4], [3, 5, 6], [3, 5], [3, 6], [3], [4, 5, 6], [4, 5], [4, 6], [4], [5, 6], [5], [6], []] 

Это мой подход с lambdaми.

 public static  Set> powerSet(T[] set) { return IntStream .range(0, (int) Math.pow(2, set.length)) .parallel() //performance improvement .mapToObj(e -> IntStream.range(0, set.length).filter(i -> (e & (0b1 << i)) != 0).mapToObj(i -> set[i]).collect(Collectors.toSet())) .map(Function.identity()) .collect(Collectors.toSet()); } 

Или параллельно (см. Комментарий parallel ()):

Размер входного набора: 18

Логические процессоры: 8 3,4 ГГц

Повышение эффективности: 30%

Подмножеством t является любое множество, которое можно сделать, удалив ноль или более элементов из t. Подмножество withoutFirst добавляет подмножества t, которые не имеют первого элемента, а цикл for будет обрабатывать добавление подмножеств с первым элементом. Например, если t содержит элементы [“1”, “2”, “3”], missingFirst будет добавлять [[“”], [“2”], [“3”], [“2”, “3 “]], а цикл for будет придерживаться” 1 “перед этим элементом и добавить его в newSet. Таким образом, мы получим [[“”], [“1”], [“2”], [“3”], [“1”, “2”], [“1”, “3”] , [“2”, “3”], [“1”, “2”, “3”]].

 public static Set> allSubsets(Set t) { Set> powerSet = new TreeSet<>(); if(t.isEmpty()) { powerSet.add(new TreeSet<>()); return powerSet; } String first = t.get(0); Set> withoutFirst = allSubsets(t.subSet(1, t.size())); for (List 1st : withoutFirst) { Set newSet = new TreeSet<>(); newSet.add(first); newSet.addAll(lst); powerSet.add(newSet); } powerSet.addAll(withoutFirst); return powerSet; } 
 // input: S // output: P // S = [1,2] // P = [], [1], [2], [1,2] public static void main(String[] args) { String input = args[0]; String[] S = input.split(","); String[] P = getPowerSet(S); if (P.length == Math.pow(2, S.length)) { for (String s : P) { System.out.print("[" + s + "],"); } } else { System.out.println("Results are incorrect"); } } private static String[] getPowerSet(String[] s) { if (s.length == 1) { return new String[] { "", s[0] }; } else { String[] subP1 = getPowerSet(Arrays.copyOfRange(s, 1, s.length)); String[] subP2 = new String[subP1.length]; for (int i = 0; i < subP1.length; i++) { subP2[i] = s[0] + subP1[i]; } String[] P = new String[subP1.length + subP2.length]; System.arraycopy(subP1, 0, P, 0, subP1.length); System.arraycopy(subP2, 0, P, subP1.length, subP2.length); return P; } } 

Недавно мне пришлось использовать что-то вроде этого, но сначала нужны самые маленькие подсписки (с одним элементом, затем двумя элементами …). Я не хотел включать пустой или весь список. Кроме того, мне не нужен список всех возвращенных подписок, мне просто нужно было поработать с каждым.

Хотелось сделать это без рекурсии и придумал следующее (с «делом», абстрагированным в функциональный интерфейс):

 @FunctionalInterface interface ListHandler { void handle(List list); } public static  void forAllSubLists(final List list, ListHandler handler) { int ll = list.size(); // Length of original list int ci[] = new int[ll]; // Array for list indices List sub = new ArrayList<>(ll); // The sublist List uml = Collections.unmodifiableList(sub); // For passing to handler for (int gl = 1, gm; gl <= ll; gl++) { // Subgroup length 1 .. n-1 gm = 0; ci[0] = -1; sub.add(null); // Some inits, and ensure sublist is at least gl items long do { ci[gm]++; // Get the next item for this member if (ci[gm] > ll - gl + gm) { // Exhausted all possibilities for this position gm--; continue; // Continue with the next value for the previous member } sub.set(gm, list.get(ci[gm])); // Set the corresponding member in the sublist if (gm == gl - 1) { // Ok, a sublist with length gl handler.handle(uml); // Handle it } else { ci[gm + 1] = ci[gm]; // Starting value for next member is this gm++; // Continue with the next member } } while (gm >= 0); // Finished cycling through all possibilities } // Next subgroup length } 

Таким образом, его также легко ограничить подсписками определенной длины.

Еще одна примерная реализация:

  public static void main(String args[]) { int[] arr = new int[]{1,2,3,4}; // Assuming that number of sets are in integer range int totalSets = (int)Math.pow(2,arr.length); for(int i=0;i 
 public class PowerSet { public static List> powerset(int[] a) { LinkedList> sets = new LinkedList>(); int n = a.length; for (int i = 0; i < 1 << n; i++) { HashSet set = new HashSet(); for (int j = 0; j < n; j++) { if ((1 << j & i) > 0) set.add(a[j]); } sets.add(set); } return sets; } public static void main(String[] args) { List> sets = PowerSet.powerset(new int[]{ 1, 2, 3 }); for (HashSet set : sets) { for (int i : set) System.out.print(i); System.out.println(); } } } 

Еще одно решение – с java8 + streaming api. Оно лениво и упорядочено, поэтому оно возвращает правильные подмножества, когда оно используется с «limit ()».

  public long bitRangeMin(int size, int bitCount){ BitSet bs = new BitSet(size); bs.set(0, bitCount); return bs.toLongArray()[0]; } public long bitRangeMax(int size, int bitCount){ BitSet bs = BitSet.valueOf(new long[]{0}); bs.set(size - bitCount, size); return bs.toLongArray()[0]; } public  Stream> powerSet(Collection data) { List list = new LinkedHashSet<>(data).stream().collect(Collectors.toList()); Stream head = LongStream.of(0).mapToObj( i -> BitSet.valueOf(new long[]{i})); Stream tail = IntStream.rangeClosed(1, list.size()) .boxed() .flatMap( v1 -> LongStream.rangeClosed( bitRangeMin(list.size(), v1), bitRangeMax(list.size(), v1)) .mapToObj(v2 -> BitSet.valueOf(new long[]{v2})) .filter( bs -> bs.cardinality() == v1)); return Stream.concat(head, tail) .map( bs -> bs .stream() .mapToObj(list::get) .collect(Collectors.toList())); } 

И клиентский код

 @Test public void testPowerSetOfGivenCollection(){ List data = new LinkedList<>(); for(char i = 'a'; i < 'a'+5; i++ ){ data.add(i); } powerSet(data) .limit(9) .forEach(System.out::print); } 

/ * Отпечатки: [] [a] [b] [c] [d] [e] [a, b] [a, c] [b, c] * /

Мы могли бы написать набор мощности с использованием или без использования рекурсии. Вот попытка без рекурсии:

 public List> getPowerSet(List set) { List> powerSet = new ArrayList>(); int max = 1 << set.size(); for(int i=0; i < max; i++) { List subSet = getSubSet(i, set); powerSet.add(subSet); } return powerSet; } private List getSubSet(int p, List set) { List subSet = new ArrayList(); int position = 0; for(int i=p; i > 0; i >>= 1) { if((i & 1) == 1) { subSet.add(set.get(position)); } position++; } return subSet; } 

Здесь нужно сгенерировать набор мощности. Идея первая = S[0] а меньшие множества – S[1,...n] .

Вычислите все подмножества smallSet и поместите их в allsubsets.

Для каждого подмножества в allsubsets, клонируйте его и добавьте сначала к подмножеству.

 ArrayList> getSubsets(ArrayList set, int index){ ArrayList> allsubsets; if(set.size() == index){ allsubsets = new ArrayList>(); allsubsets.add(new ArrayList()); // the empty set }else{ allsubsets = getSubsets(set, index+1); int item = set.get(index); ArrayList> moresubsets = new ArrayList>(); for(ArrayList subset: allsubsets){ ArrayList newsubset = new ArrayList(); newsubset.addAll(subset); newsubset.add(item); moresubsets.add(newsubset); } moresubsets.addAll(moresubsets); } return allsubsets; } 
  • Могу ли я передать аргумент VBScript (файл vbs, запущенный cscript)?
  • Когда используется ключевое слово C # ref всегда хорошая идея?
  • Возможны ли параметры в strings.xml?
  • Почему бы не вывести параметр шаблона из конструктора?
  • Передайте параметр EventHandler
  • Как проверить, указан ли аргумент метода директивы в AngularJS?
  • Как назначить «ссылку» на поле classа в c #?
  • C #: как - одно приложение экземпляра, которое принимает новые параметры?
  • Значение, переданное с request.setAttribute (), недоступно request.getParameter ()
  • Как написать сценарий bash, который принимает необязательные входные аргументы?
  • Давайте будем гением компьютера.