Integer Percentify


21

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

Все целые числа в возвращаемом списке должны в точности составлять до 100. Вы можете предположить, что сумма переданных целых чисел больше 0. Как вы хотите округлить или усечь десятичные дроби, зависит от вас, пока любое единственное полученное целое число возвращается в процентах выключен не более чем на 1 в любом направлении.

p([1,0,2])      ->  [33,0,67] or [34,0,66]
p([1000,1000])  ->  [50,50]
p([1,1,2,4])    ->  [12,12,25,51] or [13,12,25,50] or [12,13,25,50] or [12,12,26,50]
p([0,0,0,5,0])  ->  [0,0,0,100,0]

Это , поэтому выигрывает самый короткий код в байтах!


Должен ли наш алгоритм быть детерминированным? Должен ли он всегда заканчиваться в течение ограниченного времени?
lirtosiast

У нас уже была похожая, но более общая проблема округления
edc65 10.10.15

1
Я предлагаю вам добавить еще один тестовый пример: p([2,2,2,2,2,3]). У него много возможных юридических ответов, но не все 2могут быть сопоставлены с одним и тем же значением. Это исключает множество чрезмерно простых алгоритмов, которые работают во всех предыдущих тестовых случаях, потому что округление не так уж плохо.
София Лехнер

4
Может p([1000,1000]) -> [49,51]?
м2

1
@ l4m2 Кажется неправильным, но оба результата отклоняются на 1 и не более, так что это соответствует спецификации
edc65

Ответы:


20

Дьялог АПЛ, 21 19 16 байт

+\⍣¯1∘⌊100×+\÷+/

Выше приведен эквивалент поезда

{+\⍣¯1⌊100×+\⍵÷+/⍵}

Попробуйте онлайн.

Как это устроено

                 ⍝ Sample input: 1 1 2 4
           +\    ⍝ Cumulative sum of input. (1 2 4 8)
              +/ ⍝ Sum of input. (8)
             ÷   ⍝ Divide the first result by the second. (0.125 0.25 0.5 1)
       100×      ⍝ Multiply each quotient by 100. (12.5 25 50 100)
      ⌊          ⍝ Round the products down to the nearest integer... (12 25 50 100)
     ∘           ⍝ and ...
  ⍣¯1            ⍝ apply the inverse of...
+\               ⍝ the cumulative sum. (12 13 25 50)

9
Если бы только Ферма мог взять у вас уроки гольфа.
TessellatingHeckler

1
@ TessellatingHeckler Я вижу, что вы там сделали. Может быть, тогда у него будет достаточно места на полях для его доказательства. :)
mbomb007

14

TI-BASIC, 26 23 16 байтов

Для калькуляторов серии TI-83 + / 84 +.

ΔList(augment({0},int(cumSum(ᴇ2Ans/sum(Ans

Спасибо @Dennis за прекрасный алгоритм! Мы берем совокупную сумму списка после пересчета в проценты, затем пол, ставим 0 на передний план и берем различия. ᴇ2на один байт короче 100.

При этом количество байтов равно:

ΔList(augment({0},int(cumSum(Ans/sum(Ans%

Интересный факт: %это двухбайтовый токен, который умножает число на 0,01, но нет способа ввести его в калькулятор! Вам нужно либо отредактировать исходный код снаружи, либо использовать программу сборки.

Старый код:

int(ᴇ2Ans/sum(Ans
Ans+(ᴇ2-sum(Ans)≥cumSum(1 or Ans

Первая строка вычисляет все проценты, а вторая строка добавляет 1 к первым Nэлементам, где Nпроцент остался. cumSum(расшифровывается как «накопленная сумма».

Пример с {1,1,2,4}:

          sum(Ans                  ; 8
int(ᴇ2Ans/                         ; {12,12,25,50}

                        1 or Ans   ; {1,1,1,1}
                 cumSum(           ; {1,2,3,4}
     ᴇ2-sum(Ans)                   ; 1
                ≥                  ; {1,0,0,0}
Ans+                               ; {13,12,25,50}

Мы не будем иметь N>dim([list], потому что ни один процент не уменьшается более чем на 1 в настиле.


Не уверен, как вы подсчитываете байты здесь, для меня это выглядит ужасно длиннее 23
Дэвид Аренбург

@DavidArenburg Это просто читабельная форма. Все жетоны ( int(, sum(, Ansи т.д.) занимают только один байт.
Деннис

4
+1 Это один из самых впечатляющих гольфов TI-BASIC, которые я видел на этом сайте.
PhiNotPi

Томас, этот ответ потрясающий!
DaveAlger

Вы уверены, что нет способа ввести %символ? Я бы подумал, что это можно найти в каталоге символов ... Кроме того, я должен достать свой TI-84 + Silver. Я не использовал это некоторое время. Блок Чувак это круто.
mbomb007

7

CJam, 25 23 22 байта

{_:e2\:+f/_:+100-Xb.+}

Спасибо @ Sp3000 за 25 → 24.

Попробуйте онлайн.

Как это устроено

_                   e# Push a copy of the input.
 :e2                e# Apply e2 to each integer, i.e., multiply by 100.
    \               e# Swap the result with the original.
     :+             e# Add all integers from input.
       f/           e# Divide the product by the sum. (integer division)
        _:+         e# Push the sum of copy.
           100-     e# Subtract 100. Let's call the result d.
               Xb   e# Convert to base 1, i.e., push an array of |d| 1's.
                 .+ e# Vectorized sum; increment the first |d| integers.

5

Mathematica, 41 байт

(s=Floor[100#/Tr@#];s[[;;100-Tr@s]]++;s)&

Подождите, что здесь происходит?
Seequ

@Seeq Алгоритм подобен старому коду в ответе TI-BASIC. Он рассчитывает все проценты по этажам, а затем добавляет 1 к первым Nэлементам, где Nпроцент остался.
алефальфа

5

J (8,04 бета) , 59 байт (30 украденных байт)

30-байтовый буквенный J-порт ответа APL Денниса :

    f=.3 :'+/\^:_1<.100*(+/\%+/)y'

    f 1 1 2 4
12 13 25 50

Ответ 59 байтов, лучшее, что я мог сделать сам:

f=.3 :0
p=.<.100*y%+/y
r=.100-+/p
p+((r$1),(#p-r)$0)/:\:p
)

(Исходя из того, что остаток должен идти к самым высоким значениям, не более +1 каждое, разделить на несколько значений в случае остатка> 1 или привязки для самого высокого значения).

например

   f 1 0 2
33 0 67

   f 1000 1000
50 50

   f 1 1 2 4
12 12 25 51

   f 0 0 0 5 0
0 0 0 100 0

   f 16 16 16 16 16 16
17 17 17 17 16 16

   f 0 100 5 0 7 1
0 89 4 0 7 0

объяснение

  • f=.3 : 0 - 'f' - это переменная типа глагола (3), определенная ниже (: 0):
  • p=. переменная 'p', построенная из:
    • y это список номеров 1 0 2
    • +/y '+' ставится между каждым значением '/', сумма списка 3
    • y % (+/y) Исходные значения y делятся на сумму: 0.333333 0 0.666667
    • 100 * (y%+/y)в 100 раз больше этих значений: 33.33.. 0 0.66...чтобы получить проценты.
    • <. (100*y%+/y) применяется ли оператор пола к процентам: 33 0 66
  • r=. переменная 'r', построенная из:
    • +/p сумма суммированных процентов: 99
    • 100 - (+/p) is 100 - сумма или оставшиеся процентные пункты, необходимые для того, чтобы сделать процентную сумму равной 100.
  • результат, не сохраняется:
    • r $ 1 это список единиц, если количество элементов нам нужно увеличить: 1 [1 1 ..]
    • #p длина списка процентов
    • (#p - r) это количество элементов, которые не будут увеличены
    • (#p-r) $ 0 список 0, если это количество: 0 0 [0 ..]
    • ((r$1) , (#p-r)$0) список 1s, за которым следует список 0s: 1 0 0
    • \: pэто список индексов, чтобы взять pего в порядке убывания.
    • /: (\:p)это список индексов, чтобы взять \:pего в порядке возрастания
    • ((r$1),(#p-r)$0)/:\:pпринимают элементы из 1 1 .. 0 0 .. список маски и сортировки так есть 1s в позициях крупнейших процентов, по одному для каждого номера , мы должны приращению и 0s для других чисел: 0 0 1.
    • p + ((r$1),(#p-r)$0)/:\:p это проценты + маска, чтобы составить список результатов с суммой 100%, которая является возвращаемым значением функции.

например

33 0 66 sums to 99
100 - 99 = 1
1x1 , (3-1)x0 = 1, 0 0
sorted mask   = 0 0 1

33 0 66
 0 0  1
-------
33 0 67

а также

  • ) конец определения.

Я не очень опытен с J; Я не был бы слишком удивлен, если бы была встроена операция «превратить список в проценты от общей суммы», а также более чистый способ «увеличить n больших значений». (Это на 11 байт меньше, чем моя первая попытка).


1
Очень круто. У меня есть решение на Python, но оно намного длиннее этого. Хорошо сделано!
DaveAlger

1
Если вы не заметили, правила изменились, поэтому вы сможете значительно сократить это.
lirtosiast

@DaveAlger спасибо! @ThomasKwa Я заметил, я не уверен, что это сильно мне помогает - с первой попытки я могу получить -2 символа. Мне нужно изменить list[0:100-n] + list[:-100-n]подход - и я не думал о другом способе подойти к нему.
TessellatingHeckler

4

JavaScript (ES6), 81 байт

a=>(e=0,a.map(c=>((e+=(f=c/a.reduce((c,d)=>c+d)*100)%1),f+(e>.999?(e--,1):0)|0)))

Это условие «должно быть равно 100» (вместо округления и суммирования) почти удвоило мой код (с 44 до 81). Хитрость заключалась в том, чтобы добавить банк для десятичных значений, который, как только он достигает 1, берет 1 из себя и добавляет его к текущему числу. Проблема тогда была с плавающей запятой, что означает, что что-то вроде [1,1,1] оставляет остаток от .9999999999999858. Поэтому я изменил чек, чтобы он был больше 0,999, и решил назвать его достаточно точным.


4

Haskell, 42 27 байт

p a=[div(100*x)$sum a|x<-a]

Практически тривиальный метод в Haskell, с несколькими пробелами, удаленными для игры в гольф.

Консоль (скобки включены, чтобы соответствовать примеру):

*Main> p([1,0,2])
[33,0,66]
*Main> p([1000,1000])
[50,50]
*Main> p([1,1,2,4])
[12,12,25,50]
*Main> p([0,0,0,5,0])
[0,0,0,100,0]

Изменить: попрактиковался в моем положить, сделал некоторые очевидные замены.

Оригинал:

p xs=[div(x*100)tot|x<-xs]where tot=sum xs

1
Сумма списка должна быть 100. В вашем первом примере это 99
Дэмиен

4

Желе , 7 байт

-2 спасибо Деннису, напомнившему мне использовать другую новую функцию ( Ä) и использовать :вместо того, что у меня было изначально.

ŻÄ׳:SI

Попробуйте онлайн!

Желе , 11 байт

0;+\÷S×ȷ2ḞI

Попробуйте онлайн!

Совершено рядом с caird coinheringaahing и пользователем 202729 в чате .

Как это устроено

0; + \ ÷ S × ȷ2ḞI - Полная программа.

0; - Добавить 0.
  + \ - накопленная сумма.
    ÷ S - делится на сумму входных данных.
      × ȷ2 - умноженное на 100. Заменено на ³ в версии с монадической ссылкой.
         ḞI - Пол каждого, вычислить приращения (дельты, различия).


3

Perl, 42 байта

На основе алгоритма Денниса

Включает +1 для -p

Запустить со списком номеров на STDIN, например

perl -p percent.pl <<< "1 0 2"

percent.pl:

s%\d+%-$-+($-=$a+=$&*100/eval y/ /+/r)%eg


2

Python 2, 89 байт

def f(L):
 p=[int(x/0.01/sum(L))for x in L]
 for i in range(100-sum(p)):p[i]+=1
 return p

print f([16,16,16,16,16,16])
print f([1,0,2])

->

[17, 17, 17, 17, 16, 16]
[34, 0, 66]

2

Brain-Flak , 150 байт

((([]){[{}]({}<>)<>([])}{})[()])<>([]){{}<>([{}()]({}<([()])>)<>(((((({})({})({})){}){}){}{}){}){}(<()>))<>{(({}<({}())>)){({}[()])<>}{}}{}([])}<>{}{}

Попробуйте онлайн!

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

(

  # Compute and push sum of numbers
  (([]){[{}]({}<>)<>([])}{})

# And push sum-1 above it (simulating a zero result from the mod function)
[()])

<>

# While elements remain
([]){{}

  # Finish computation of modulo from previous step
  <>([{}()]({}

    # Push -1 below sum (initial value of quotient in divmod)
    <([()])>

  # Add to 100*current number, and push zero below it
  )<>(((((({})({})({})){}){}){}{}){}){}(<()>))

  # Compute divmod
  <>{(({}<({}())>)){({}[()])<>}{}}{}

([])}

# Move to result stack and remove values left over from mod
<>{}{}

2

JavaScript (ES6) 60 63 95

Адаптировано и упрощено из моего (неправильного) ответа на другой вопрос
Thk на @ l4m2 за обнаружение, что это тоже неправильно

Исправлено сохранение 1 байта (и 2 байта меньше, не считая имени F=)

v=>v.map(x=>(x=r+x*100,r=x%f,x/f|0),f=eval(v.join`+`),r=f/2)

Попробуйте запустить приведенный ниже фрагмент в любом браузере, совместимом с EcmaScript 6

F=
v=>v.map(x=>(x=r+x*100,r=x%f,x/f|0),f=eval(v.join`+`),r=f/2)

console.log('[1,0,2] (exp [33,0,67] [34,0,66])-> '+F([1,0,2]))
console.log('[1000,1000] (exp [50,50])-> '+F([1000,1000]))
console.log('[1,1,2,4] (exp[12,12,25,51] [13,12,25,50] [12,13,25,50] [12,12,26,50])-> '+F([1,1,2,4]))
console.log('[0,0,0,5,0] (exp [0,0,0,100,0])-> '+F([0,0,0,5,0]))
console.log('[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,980] -> '+F([1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,980]))
console.log('[2,2,2,2,2,3] -> ' + F([2,2,2,2,2,3]))
<pre id=O></pre>


Не удалось[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,980]
l4m2

@ l4m2 не удалось почему? Сумма 100 иany single resulting integer returned as a percentage is off by no more than 1 in either direction.
edc65

Последний должен быть максимум один на 98, но это 100
l4m2

@ l4m2, да, спасибо. Время подумать еще раз
edc65

@ l4m2 должен быть исправлен сейчас
edc65

1

Ржавчина, 85 байт

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

let a=|c:Vec<_>|c.iter().map(|m|m*100/c.iter().fold(0,|a,x|a+x)).collect::<Vec<_>>();

1

JavaScript, 48 байт

F=

x=>x.map(t=>s+=t,y=s=0).map(t=>-y+(y=100*t/s|0))

// Test 
console.log=x=>O.innerHTML+=x+'\n';


console.log('[1,0,2] (exp [33,0,67] [34,0,66])-> '+F([1,0,2]))
console.log('[1000,1000] (exp [50,50])-> '+F([1000,1000]))
console.log('[1,1,2,4] (exp[12,12,25,51] [13,12,25,50] [12,13,25,50] [12,12,26,50])-> '+F([1,1,2,4]))
console.log('[0,0,0,5,0] (exp [0,0,0,100,0])-> '+F([0,0,0,5,0]))
<pre id=O></pre>


0

Jq 1,5 , 46 байт

add as$s|.[1:]|map(100*./$s|floor)|[100-add]+.

расширенный

  add as $s               # compute sum of array elements
| .[1:]                   # \ compute percentage for all elements 
| map(100*./$s|floor)     # / after the first element
| [100-add] + .           # compute first element as remaining percentage

Попробуйте онлайн!


0

PHP, 82 байта

for(;++$i<$argc-1;$s+=$x)echo$x=$argv[$i]/array_sum($argv)*100+.5|0,_;echo 100-$s;

принимает входные данные из аргументов командной строки, печатает проценты, разделенные подчеркиванием.

Запустите -nrили попробуйте онлайн .


Это выводится, 15_15_15_15_15_25когда дается вход [2,2,2,2,3], что не правильно, потому что3/13 ~= 23.1%
София Лехнер

@SophiaLechner Какой из ответов делает это правильно?
Тит

Большинство, на самом деле. До сих пор правильные ответы строятся вокруг одного из двух алгоритмов; первое округляет проценты кумулятивных сумм и учитывает разницу; вторая вычисляет минимальные значения процентов, а затем увеличивает достаточно четкие проценты, чтобы довести общее количество до 100.
София Лехнер

@SophiaLechner Я не имел в виду, что не буду смотреть на это; но я сделаю позже. Спасибо, что заметили.
Тит
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.