1. Java / Говнокод #7547

    +76

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    51. 51
    52. 52
    53. 53
    54. 54
    55. 55
    56. 56
    57. 57
    58. 58
    59. 59
    60. 60
    61. 61
    62. 62
    63. 63
    64. 64
    65. 65
    66. 66
    67. 67
    68. 68
    69. 69
    70. 70
    71. 71
    72. 72
    73. 73
    74. 74
    75. 75
    76. 76
    77. 77
    78. 78
    79. 79
    80. 80
    81. 81
    82. 82
    83. 83
    84. 84
    85. 85
    86. 86
    87. 87
    88. 88
    89. 89
    90. 90
    91. 91
    92. 92
    93. 93
    class PseudoVamp {
    	public int num;
    	public boolean truevamp = false;
    	public int x;
    	public int y;
    	public int n1;
    	public int n2;
    	public int n3;
    	public int n4;
    
    	void breaknsort() {
    		n1 = num % 10;
    		n2 = num / 10 % 10;
    		n3 = num / 100 % 10;
    		n4 = num / 1000;
    		int tmp;
    		for (int i = 0; i < 4; i++) {
    			if (n1 > n2) {
    				tmp = n1;
    				n1 = n2;
    				n2 = tmp;
    			}
    
    			if (n2 > n3) {
    				tmp = n2;
    				n2 = n3;
    				n3 = tmp;
    			}
    
    			if (n3 > n4) {
    				tmp = n3;
    				n3 = n4;
    				n4 = tmp;
    			}
    		}
    	}
    
    	public PseudoVamp(int a, int b) {
    		x = a;
    		y = b;
    		num = x * y;
    		breaknsort();
    	}
    }
    
    public class Test {
    
    	static void checkvamp(PseudoVamp vamp) {
    		int x1 = vamp.x % 10;
    		int x2 = vamp.x / 10;
    
    		int y1 = vamp.y % 10;
    		int y2 = vamp.y / 10;
    
    		int tmp;
    		for (int i = 0; i < 4; i++) {
    			if (x1 > x2) {
    				tmp = x1;
    				x1 = x2;
    				x2 = tmp;
    			}
    
    			if (x2 > y1) {
    				tmp = x2;
    				x2 = y1;
    				y1 = tmp;
    			}
    
    			if (y1 > y2) {
    				tmp = y1;
    				y1 = y2;
    				y2 = tmp;
    			}
    		}
    		if (vamp.n1 == x1 && vamp.n2 == x2 && vamp.n3 == y1 && vamp.n4 == y2)
    			vamp.truevamp = true;
    	}
    
    	public static void main(String[] args) {
    		for (int i = 11; i < 100; i++) {
    			for (int j = 11; j < 100; j++) {
    				PseudoVamp v = new PseudoVamp(i, j);
    				if (v.num < 1000)
    					continue;
    				if (v.num > 9999)
    					return;
    				checkvamp(v);
    				if (v.truevamp)
    				System.out.println(v.x + " * " + v.y + " = " + v.num);
    			}
    		}
    	}
    }

    A vampire number has an even number of digits and is formed by multiplying a pair of numbers containing half the number of digits of the result. The digits are taken from the original number in any order. Pairs of trailing zeroes are not allowed. Examples include:
    1260 = 21 * 60
    1827 = 21 * 87
    2187 = 27 * 81
    Write a program that finds all the 4-digit vampire numbers.
    w/o using of arrays.

    Запостил: ch0sen, 15 Августа 2011

    Комментарии (43) RSS

    • ко мне, псевдоупыри, ко мне, псевдовурдалаки!

      так что, новая формула для гетов, товарищи.
      Ответить
    • Вроде 14.
      import Data.List (sort)
      
      makeKey :: [Int] -> String
      makeKey xs = sort $ concat $ map show xs
      
      isVampirePair :: Int -> Int -> Bool
      isVampirePair x y = (makeKey [x * y]) == (makeKey [x,y])
      
      vampireNumbers :: Int -> Int -> [Int]
      vampireNumbers from to = [x * y | x <- [from..to], y <- [from..to], isVampirePair x y]
      
      main = do
           putStrLn $ "Number of vampires numbers: " ++ (show $ length $ vampireNumbers 10 99)
      Ответить
      • Умножение имеет паршивое свойство: коммутативность. Добавьте после length, что-нибудь похожее на unique.
        Ответить
      • А, не, забыл убрать дубликаты. Итого 7:
        import Data.List (sort, nub)
        
        makeKey :: [Int] -> String
        makeKey xs = sort $ concat $ map show xs
        
        isVampirePair :: Int -> Int -> Bool
        isVampirePair x y = (makeKey [x * y]) == (makeKey [x,y])
        
        vampireNumbers :: Int -> Int -> [Int]
        vampireNumbers from to = nub [x * y | x <- [from..to], y <- [from..to], isVampirePair x y]
        
        main = do
             putStrLn $	"Vampire numbers: " ++ show (vampireNumbers 10 99)
             putStrLn $ "Number of vampires numbers: " ++ (show $ length $ vampireNumbers 10 99)

        Вывод:
        Vampire numbers: [1395,1260,1827,2187,1530,1435,6880]
        Number of vampires numbers: 7
        Ответить
        • У меня так же получилось, но опять же с нарушением условия - я использовал массивы/списки (в вашем случае даже больше, ведь строки в Haskell - это списки). А как без них? Есть идея использовать регулярки, но это читерство опять же. Думаю, задачу решить только с использованием чисел.
          Ответить
        • Да, преобразуя число в строку, а потом сравнивая 2 сортированных массива символов получается очень элегантное решение.
          Но гораздо интересней работать только с числами, без массивов / списков. Попробуйте =)
          Не "говнорешение" у меня укладывается в 28 строк.
          Ответить
          • Если есть какой-то интересный теоретико-числовой способ проверить одинаковый состав цифр в множителях и в произведении (или получать потенциальные "множители" из самого числа), поведайте нам, пожалуйста (я всю голову сломал). Если "w\o arrays" означает просто вычислять остатки от деления и сортировать цифры самостоятельно, меня такое решение не очень интересует.
            Ответить
            • Ну пузырьков тут нет :-)
              Но "самостоятельность" все равно в таком случае никуда не убрать:
              public class VampireNumbers {
              	static int toNum(int i, int j) {
              		return (i * 10) + j;
              	}
              	static void check(int i, int m, int n) {
              		if (m * n == i)	System.out.println(i + " = " + m + " * " + n);
              	}
              	public static void main(String[] args) {
              		for (int i = 1001; i < 10000; i++) {
              			int n1 = i % 10;
              			int n2 = i / 10 % 10;
              			int n3 = i / 100 % 10;
              			int n4 = i / 1000;
              			check(i, toNum(n1, n2), toNum(n3, n4));
              			check(i, toNum(n1, n2), toNum(n4, n3));
              			check(i, toNum(n1, n3), toNum(n2, n4));
              			check(i, toNum(n1, n3), toNum(n4, n2));
              			check(i, toNum(n1, n4), toNum(n2, n3));
              			check(i, toNum(n1, n4), toNum(n3, n2));
              			check(i, toNum(n2, n1), toNum(n3, n4));
              			check(i, toNum(n2, n1), toNum(n4, n3));
              			check(i, toNum(n2, n3), toNum(n4, n1));
              			check(i, toNum(n2, n4), toNum(n3, n1));
              			check(i, toNum(n3, n1), toNum(n4, n2));
              			check(i, toNum(n3, n2), toNum(n4, n1));
              		}
              	}
              }
              Ответить
              • Да, я думал о таком, неплохой вариант. Производительность будет значительно выше, сложность - ниже. Жаль только плохо масштабируется.
                Ответить
                • А оно вобще не маштабируется =)
                  это решение конкретной задачи. Для маштабирования нужны списки полюбому.
                  Ответить
              • Это копипаста.

                Вот математический способ проверить, совпадают ли все цифры в двух числах (JS):
                // order - количество сравниваемых цирф
                // работает только на положительных
                function digitsEqual(order, n1, n2) {
                  var matchedDigitsN1 = 0;
                  var matchedDigitsN2 = 0;
                  var matchedAtLeastOnce = false;
                  
                  for (var i=0; i<order; i++) {
                    var currentDigitOrderN1 = Math.pow(10,i);
                    var nextDigitOrderN1 = Math.pow(10,i+1);
                    var currentDigitN1 = Math.floor(n1/currentDigitOrderN1)-Math.floor(n1/nextDigitOrderN1)*10;
                    
                    for (var j=0; j<order; j++) {
                      if ((matchedDigitsN2 & Math.pow(2,j)) > 0) {
                        continue;
                      }
                      var currentDigitOrderN2 = Math.pow(10,j);
                      var nextDigitOrderN2 = Math.pow(10,j+1);
                      var currentDigitN2 = Math.floor(n2/currentDigitOrderN2)-Math.floor(n2/nextDigitOrderN2)*10;
                      
                      if ( ((matchedDigitsN1 & Math.pow(2,i)) == 0) && (currentDigitN1 == currentDigitN2) ) {
                        matchedDigitsN1 = matchedDigitsN1 | Math.pow(2,i);
                        matchedDigitsN2 = matchedDigitsN2 | Math.pow(2,j);
                        matchedAtLeastOnce = true;
                        break;
                      }
                    }
                  }
                  
                  return (matchedDigitsN1 == matchedDigitsN2) && matchedAtLeastOnce;
                }


                Вот тесты:
                console.log(digitsEqual(4,1234,1234));
                console.log(digitsEqual(4,1234,4321));
                console.log(digitsEqual(4,1230,1023));
                console.log(digitsEqual(4,1010,1100));
                console.log(digitsEqual(4,110,1100));
                console.log(digitsEqual(4,0,0));
                console.log(!digitsEqual(4,0,1));
                console.log(!digitsEqual(4,1111,1110));
                console.log(!digitsEqual(4,1122,2011));
                console.log(!digitsEqual(4,9999,0));
                Ответить
                • умножения, целочисленные деления, вычеты... Прям как в школе про системы счисления...
                  уж не проще ли обойтись без математики, а обращаться с числами как со строками?
                  Ответить
                  • Ага, и доступ к символам получать по индексу? ;) Я уже писал выше на тему "строка - это массив символов".
                    Ответить
                • Этот пост нужно было оформить в виде обособленного и суверенного ГК.
                  Ответить
                  • А как можно с учетом изложенных выше ограничений написать лучше? Прошу по пунктам разобрать мои ошибки, если не затруднит.
                    Ответить
                    • код понятен только автору, вот в чём основная ошибка.
                      Ответить
                      • Думаю, что разобраться в нем можно. В реальном проекте я бы комментариев дописал... наверное :) Во всяком случае переменные лучше вынести в начало процедуры и описать предназначение каждой (хотя я старался давать наиболее осмысленные имена). Тогда алгоритм будет виднее.

                        Видите ли, ваш код на Haskell неподготовленному человеку еще менее понятен. Хотя, конечно, я считаю такой код в функциональном стиле более выразительным, чем та императивная каша, что я написал.
                        Ответить
                        • На самом деле код в хаскеле я понял сразу, хотя хаскела я в жизни не видал ...
                          а вот JS вроде как и лучше дожен воспринимать, так наоборот нифига не понял каким образом оно решает поставленную задачу...
                          Ответить
                          • haskell не так труден, как кажется. Программирование на нём доставляет огромное удовольствие. Пленяет строгая типизация и минимальное кол-во строк, необходимое для решения задачи в сочетании с довольно высокой скоростью работы. И самое удивительное, что очень часто работает правило "Компилируется - значит, работает правильно".
                            Ответить
                          • При использовании того же алгоритма JS выглядел бы проще. У меня решение совсем иным способом, на побитовой арифметике.
                            Ответить
                        • Разобраться в нём можно, не спорю. Однако потребуется немало пунктов мозгосилы. Особо порадовала идея использовать числа как небольшие битовые карты =) От order можно избавиться, вычисляя порядки чисел внутри функции (если порядки разные, можно сразу возвращать false).
                          Ответить
                          • >(если порядки разные, можно сразу возвращать false)
                            С чего это? order - это число цифр в десятичной записи, которые нам нужно рассмотреть. Избавиться от него мы не можем.

                            К тому же, мы проверяем совпадение всех цифр в числе без учета их позиции, поэтому отсекать по принципу "разные порядки" мы не можем - если вы это имели в виду под "порядки разные".

                            >Особо порадовала идея использовать числа как небольшие битовые карты =)
                            Именно благодаря этому я смог уйти от массивов. Другого способа отмечать найденные совпадения я не придумал.
                            Ответить
                            • > order - это число цифр в десятичной записи
                              how much watch?
                              Ответить
                            • > Избавиться от него мы не можем.
                              Ещё как можем. Какой смысл сравнивать 12345 с 1234? Число цифр в десятичной записи у них разное, поэтому совпадать наборы цифр не могут априори. Вы и сами без труда сможете написать функцию определения числа десятичных знаков в записи числа:
                              function getOrder(base, number) {
                                  var order = 0;
                                  var product = 1;
                                  do {
                                      product *= base;
                                      order += 1;
                                  } while (product < number);
                                  return order;
                              }
                              /* getOrder(10, 12345) == 5 */


                              > Именно благодаря этому я смог уйти от массивов
                              Спасибо, кэп )
                              Ответить
                              • Разумеется, отрицательные числа в расчёт не берутся. Адаптировать под них алгоритм - секундное дело, но подробности тут излишни.
                                Ответить
                              • А, теперь я вас понял. Вы правы. Меня немного переклинило насчет leading zeros.
                                Ответить
              • Попробовал реализовать ваше решение в общем виде (с одним существенным ограничением: не отбрасываются случаи, когда у обоих множителей нули на конце), вот что получилось:
                import Data.List (permutations)
                
                strToTuple :: String -> (Integer, Integer)
                strToTuple s = let n = (length s) `div` 2
                                   a = read $ take n s
                                   b = read $ drop n s
                                   in (a, b)
                
                strToCandidates :: [String] -> [(Integer, Integer)]
                strToCandidates l = map strToTuple l
                
                candidates :: Integer -> [(Integer, Integer)]
                candidates n = if (even len)
                                 then strToCandidates $ permutations str
                                 else []
                                     where str = show n
                                           len = length str
                
                hasVampirePair :: [(Integer, Integer)] -> Integer -> Bool
                hasVampirePair l n = any (\p -> (fst p) * (snd p) == n) l
                
                vampireNumbers :: Integer -> Integer -> [Integer]
                vampireNumbers from to = [x| x <- [from..to], isVampireNumber x]
                
                main = do
                     putStrLn $ "Vampire numbers: " ++ show (vampireNumbers 1000 9999)

                Ответы выдаёт верные, но код работает очень медленно (скорее всего, его тормозит вычисление всевозможных перестановок цифр, которые у вас вычислены ручками, ибо замена Integer на Int ничего не изменила).
                Исходный вариант решает задачу за 0.012 сек, этот - за 3.253 сек.
                Решения задачи этим вариантом для размерности 6 я не дождался, исходный вариант считает задачу этой размерности 1 сек. Ваш вариант на java у меня отрабатывает за 0.035 сек.

                p.s. неделя бенчмарков на ГК
                Ответить
                • namespace govnokod.ru {
                  week PseudoTroll {}
                  }
                  Ответить
                • Проглатилось определение isVampireNumber
                  isVampireNumber n = hasVampirePair (candidates n) n
                  Ответить
                  • не разжевалось и выплюнулось? за это отдельное спасибо = )
                    Ответить
                • А вы разве не списки опять используете?
                  strToCandidates :: [String] -> [(Integer, Integer)]
                  strToCandidates l = map strToTuple l

                  Я конечно плохо незнаю хаскель, но тут явное читерство :-P
                  общего решения, мне кажется, простой арифметикой не добиться
                  Ответить
                  • у меня не было цели "не использовать массивы/списки", поскольку без них можно обойтись только для заранее известной размерности (вы очень наглядно продемонстрировали это решение). Мне было интересно сравнить производительность и простоту реализации разных решений. К моему удивлению, самый простой первый алгоритм в три строчки (объявления типов опциональны и нужны для людей /оптимизации, haskell вывел бы их самостоятельно) работает быстрее всего. Опять даёт о себе знать волшебное правило "сначала простой и понятный код, потом (возможно) - оптимизация".
                    Ответить
                  • См. мой код на JS выше - общее решение только на арифметике
                    Ответить

    Добавить комментарий