Помножте два цілісні многочлени


14

Ваше завдання полягає в тому, щоб взяти два одночленних цілих многочленних вирази і помножити їх на їх непрощене розширення першого терміна-магістраль зліва направо (AKA FOIL у випадку двочленів). Не поєднуйте вподобані терміни та не упорядковуйте результат. Щоб бути більш чітким щодо розширення, помножте перший додаток у першому виразі на кожен доданок у другому, щоб продовжувати, і продовжуйте в першому виразі до тих пір, поки всі доданки не будуть помножені на всі інші доданки. Вирази будуть надані у спрощеному варіанті LaTeX.

Кожен вираз буде послідовністю термінів, розділених +(з рівно одним пробілом на кожній стороні). Кожен термін буде відповідати наступному регулярному вираженню:

-?\d+x\^\d+

У звичайній англійській мові термін - це необов'язковий ведучий, який -супроводжується однією або кількома цифрами, за якими слідує xі негативна ціла сила (з ^)

Приклад повного виразу:

6x^3 + 1337x^2 + -4x^1 + 2x^0

Підключившись до LaTeX, ви отримуєте 6х3+1337 рікх2+-4х1+2х0

Вихід також повинен відповідати цьому формату.

Оскільки дужки не оточують експоненти в цьому форматі, LaTeX насправді відображатиме багатозначні експоненти неправильно. (наприклад, 4x^3 + -2x^14 + 54x^28 + -4x^5візуалізація як 4х3+-2х14+54х28+-4х5 ) Вам не потрібно це враховувати, і ви не повинні включати дужки у свій висновок.

Приклади тестових випадків

5x^4
3x^23

15x^27

6x^2 + 7x^1 + -2x^0
1x^2 + -2x^3

6x^4 + -12x^5 + 7x^3 + -14x^4 + -2x^2 + 4x^3

3x^1 + 5x^2 + 2x^4 + 3x^0
3x^0

9x^1 + 15x^2 + 6x^4 + 9x^0

4x^3 + -2x^14 + 54x^28 + -4x^5
-0x^7

0x^10 + 0x^21 + 0x^35 + 0x^12

4x^3 + -2x^4 + 0x^255 + -4x^5
-3x^4 + 2x^2

-12x^7 + 8x^5 + 6x^8 + -4x^6 + 0x^259 + 0x^257 + 12x^9 + -8x^7

Правила та припущення

  • Ви можете припустити, що всі входи відповідають саме цьому формату. Поведінка для будь-якого іншого формату не визначена для цілей цього виклику.
    • Слід зазначити, що будь-який спосіб взяття двох поліномів є дійсним, за умови, що обидва читаються у вигляді рядків, що відповідають наведеному вище формату.
  • Порядок поліномів має значення завдяки очікуваному порядку розширення продукту.
  • Ви повинні підтримувати вхідні коефіцієнти між 128 та 127 та вхідними показниками до 255 .
    • Коефіцієнти виходу між 16,256 та 16,384 та показниками до 510 повинні підтримуватися.
  • Можна припустити, що кожен вхідний многочлен містить не більше 16 доданків
    • Тому ви повинні (як мінімум) підтримувати до 256 термінів у висновку
  • Умови з нульовими коефіцієнтами слід залишити так, як є, при цьому показники повинні бути правильно поєднані
  • Негативний нуль дозволений на вході, але він не відрізняється від позитивного нуля семантично. Завжди виводите позитивний нуль. Не опускайте нульових доданків.

Щасливого гольфу! Удачі!



2
@LuisfelipeDejesusMunoz Я не уявляю. Розбір є невід'ємною частиною виклику, і ОП говорить - "Слід зазначити, що будь-який метод взяття в два поліноми є дійсним, за умови, що обидва читаються у вигляді рядків, що відповідають вищевказаному формату " (наголос додано)
Джузеппе

Відповіді:


4

R , 159 153 148 байт

function(P,Q,a=h(P),b=h(Q))paste0(b[1,]%o%a[1,],"x^",outer(b,a,"+")[2,,2,],collapse=" + ")
h=function(s,`/`=strsplit)sapply(el(s/" . ")/"x.",strtoi)

Спробуйте в Інтернеті!

Мені дуже хотілося скористатися outer, тому майже напевно є більш ефективний підхід.


4

Haskell , 131 122 байт

(%)=drop
f s=do(a,t)<-reads s;(i,u)<-reads$2%t;(a,i):f(3%u)
p!q=3%do(a,i)<-f p;(b,j)<-f q;" + "++shows(a*b)"x^"++show(i+j)

Спробуйте в Інтернеті!

fрозбирає многочлен з рядка, !множує два з них і форматує результат.

H.PWiz зберег 9 байт. Спасибі!

Безумовно

type Monomial = (Int, Int) -- a^i
type Polynomial = [Monomial]

parse :: String -> Polynomial
parse s = do (a, s')  <- reads s
             (i, s'') <- reads (drop 2 s')
             (a, i) : parse (drop 3 s'')

(!) :: String -> String -> String
p!q = drop 3 (concat terms)
  where terms    = [term (a*b) (i+j) | (a,i) <- p', (b,j) <- q']
        term a i = concat [" + ", show a, "x^", show i]
        p'       = parse p
        q'       = parse q



2

Рубі , 102 100 98 байт

->a,b{a.scan(w=/(.*?)x.(\d+)/).map{|x|b.scan(w).map{|y|(eval"[%s*(z=%s;%s),z+%s]"%y+=x)*"x^"}}*?+}

Спробуйте в Інтернеті!

Як?

Перший крок: отримати всі числа з обох многочленів: scanповертає числа у вигляді масиву пар рядків. Потім зробіть декартовий добуток із 2-х списків. Зараз у нас є всі номери, де вони нам потрібні, але все ще в неправильному порядку.

Приклад: якщо ми помножимо 3x^4на -5x^2, ми отримаємо числа як [["3","4"],["-5","2"]], перша ідея полягала в тому, щоб скопіювати і згладити цей список, а потім поставити числа у вираз, який слід оцінити як [3*-5, 4+2]. Насправді нам не потрібно переставляти числа, ми могли б зробити це всередині виразу, використовуючи тимчасову змінну: вираз стає [3*(z=4,-5),z+2].

Оцінивши ці вирази, ми отримуємо коефіцієнт і показник, нам потрібно приєднатися до них за допомогою "x^", а потім приєднатися до всіх тем, що використовують "+".


2

Haskell, 124 121 байт

import Data.Lists
f!x=map f.splitOn x
z=read!"x^"!"+"
a#b=drop 3$do[u,v]<-z a;[p,q]<-z b;" + "++shows(u*p)"x^"++show(v+q)

Примітка: TIO бракує Data.Lists, тому я імпортую Data.Lists.Splitта Data.List: Спробуйте в Інтернеті!

Редагувати: -3 байти завдяки @Lynn


Це насправді 123 байти! f!x=map f.splitOn xа потім z=read!"x^"!"+"зберігає байт; для останнього рядка drop 3$do[u,v]<-z a;[p,q]<-z b;" + "++shows(u*p)"x^"++show(v+q)економиться ще два. 120 байт
Лінн

1
@Lynn: версія TIO імпортує Data.Listзамість Data.Lists, тож це 1 байт.
Ними



1

Python 2 , 193 байт

import re
f=re.finditer
lambda a,b:' + '.join(' + '.join(`int(m.group(1))*int(n.group(1))`+'x^'+`int(m.group(2))+int(n.group(2))`for n in f('(-?\d+)x\^(\d+)',b))for m in f('(-?\d+)x\^(\d+)',a))

Спробуйте в Інтернеті!

Бічна примітка: Перший раз, коли ви робите виклик для гольфу з кодом, так що вибачте, якщо спроба смокче ха-ха


3
Ласкаво просимо до PPCG! Я не сильно програміст пітона, але, мабуть, є можливість для вдосконалення. Можливо, ви можете знайти допомогу в Підказки для гольфу на Python або Поради щодо гольфу на <всіх мовах> ! Сподіваюся, вам сподобається час, який ви тут проводите :-)
Джузеппе


1
Деякі швидкі гольфи на 161 байт . Хоча дивлячись на інші відповіді пітона, це re.finditerможе бути не найкоротшим підходом
Джо Кінг,

1

Сітківка , 110 байт

\S\S+(?=.*\n(.+))
 $1#$&
|" + "L$v` (-?)(\d+)x.(\d+).*?#(-?)(\d+)x.(\d+)
$1$4$.($2*$5*)x^$.($3*_$6*
--|-(0)
$1

Спробуйте в Інтернеті! Пояснення:

\S\S+(?=.*\n(.+))
 $1#$&

Префікс кожного терміна на першому вході з а #, копією другого вводу та пробілом. Це означає, що всім термінам у копіях другого входу передує пробіл, і жоден із термінів з першого вводу не є.

|" + "L$v` (-?)(\d+)x.(\d+).*?#(-?)(\d+)x.(\d+)
$1$4$.($2*$5*)x^$.($3*_$6*

Зіставити всі копії термінів на другому вході та відповідний їм термін з першого вводу. Об’єднайте будь-які -знаки, помножте коефіцієнти та додайте індекси. Нарешті з'єднайте всі отримані заміни рядком  + .

--|-(0)
$1

Видаліть будь-які пари -s і перетворіть-0 на 0.


1

SNOBOL4 (CSNOBOL4) , 192 176 байт

	P =INPUT
	Q =INPUT
	D =SPAN(-1234567890)
P	P D . K ARB D . W REM . P	:F(O)
	B =Q
B	B D . C ARB D . E REM . B	:F(P)
	O =O ' + ' K * C 'x^' W + E	:(B)
O	O ' + ' REM . OUTPUT
END

Спробуйте в Інтернеті!

	P =INPUT				;* read P
	Q =INPUT				;* read Q
	D =SPAN(-1234567890)			;* save PATTERN for Digits (or a - sign); equivalent to [0-9\\-]+
P	P D . K ARB D . W REM . P	:F(O)	;* save the Koefficient and the poWer, saving the REMainder as P, or if no match, goto O
	B =Q					;* set B = Q
B	B D . C ARB D . E REM . B	:F(P)	;* save the Coefficient and the powEr, saving the REMainder as B, or if no match, goto P
	O =O ' + ' K * C 'x^' W + E	:(B)	;* accumulate the output
O	O ' + ' REM . OUTPUT			;* match ' + ' and OUTPUT the REMainder
END



1

C # (Visual C # Interactive Compiler) , 192 190 байт

n=>m=>string.Join(g=" + ",from a in n.Split(g)from b in m.Split(g)select f(a.Split(p="x^")[0])*f(b.Split(p)[0])+p+(f(a.Split(p)[1])+f(b.Split(p)[1])));Func<string,int>f=int.Parse;string p,g;

Синтаксис запиту здається, що байт коротший, ніж синтаксис методу.

Спробуйте в Інтернеті!


Кожен вираз буде послідовністю термінів, розділених + (з рівно одним пробілом на кожній стороні) 190 байт
термін дії якого минув

1

Желе , 28 байт

ṣ”+ṣ”xV$€)p/ZPSƭ€j⁾x^Ʋ€j“ + 

Спробуйте в Інтернеті!

Повна програма. Приймає два поліноми як список двох рядків.

Пояснення (розширена форма)

ṣ”+ṣ”xV$€µ€p/ZPSƭ€j⁾x^Ʋ€j“ + ” Arguments: x
         µ                     Monadic chain.
          €                    Map the monadic link over the argument.
                               Note that this will "pop" the previous chain, so
                               it will really act as a link rather than a
                               sub-chain.
ṣ”+                             ṣ, right = '+'.
                                Split the left argument on each occurrence of
                                the right.
                                Note that strings in Jelly are lists of
                                single-character Python strings.
        €                       Map the monadic link over the argument.
       $                         Make a non-niladic monadic chain of at least
                                 two links.
   ṣ”x                            ṣ, right = 'x'.
                                  Split the left argument on each occurrence of
                                  the right.
      V                           Evaluate the argument as a niladic link.
            /                  Reduce the dyadic link over the argument.
           p                    Cartesian product of left and right arguments.
                       €       Map the monadic link over the argument.
                      Ʋ         Make a non-niladic monadic chain of at least
                                four links.
             Z                   Transpose the argument.
                 €               Map the monadic link over the argument.
                ƭ                 At the first call, call the first link. At the
                                  second call, call the second link. Rinse and
                                  repeat.
              P                    Product: ;1×/$
               S                   Sum: ;0+/$
                  j⁾x^           j, right = "x^".
                                 Put the right argument between the left one's
                                 elements and concatenate the result.
                        j“ + ” j, right = " + ".
                               Put the right argument between the left one's
                               elements and concatenate the result.

Збудження

)те саме, що µ€.
Слід слід мається на увазі і може бути опущений.

Алгоритм

Скажімо, у нас є такий вклад:

["6x^2 + 7x^1 + -2x^0", "1x^2 + -2x^3"]

Перша процедура - Парсінг, застосована до кожного з двох многочленів. Давайте розберемося з першим,"6x^2 + 7x^1 + -2x^0" :

Перший крок - розділити рядок на '+', щоб розділити доданки. Це призводить до:

["6x^2 ", " 7x^1 ", " -2x^0"]

Наступний крок - розділити кожен рядок на 'x', відокремити коефіцієнт від експонента. Результат такий:

[["6", "^2 "], [" 7", "^1 "], [" -2", "^0"]]

Наразі схоже, що в цих рядках багато сміття, але цей сміття насправді неважливо. Усі ці рядки будуть оцінені як ніладієві желейні посилання. Тривіально пробіли неважливі, оскільки вони не знаходяться між цифрами чисел. Тож ми могли б також оцінити наведене нижче і все-таки отримати той же результат:

[["6", "^2"], ["7", "^1"], ["-2", "^0"]]

У ^подивимося трохи більше заважаючи, але вони на насправді нічого не роблять , або! Що ж, ^є побітним атомом XOR, однак ніладієві ланцюги діють як монадні ланки, за винятком того, що перше посилання насправді стає аргументом, замість того, щоб брати аргумент, якщо воно є ніладичним. Якщо це не так, тоді посилання матиме аргумент 0. Експоненти мають ^s як свій перший ^знак, а не є ніладічним, тому аргумент вважається таким 0. Решта рядка, тобто число, є правильним аргументом ^. Так, наприклад, ^2є0 XOR 2=2. Очевидно,0 XOR н=н. Всі експоненти цілі, тому ми добре. Тому оцінка цього замість вищезазначеного не змінить результату:

[["6", "2"], ["7", "1"], ["-2", "0"]]

Ось і ми:

[[6, 2], [7, 1], [-2, 0]]

Цей крок також перетвориться "-0"на0 .

Оскільки ми аналізуємо обидва входи, результат після розбору буде таким:

[[[6, 2], [7, 1], [-2, 0]], [[1, 2], [-2, 3]]]

Парсинг зараз завершений. Наступна процедура - Множення.

Спочатку ми беремо декартовий продукт із цих двох списків:

[[[6, 2], [1, 2]], [[6, 2], [-2, 3]], [[7, 1], [1, 2]], [[7, 1], [-2, 3]], [[-2, 0], [1, 2]], [[-2, 0], [-2, 3]]]

Складається багато пар, кожна з яких по одному елементу з лівого списку і по одному праворуч, по порядку. Це також є цільовим порядком виводу. Цей виклик дійсно просить нас застосувати мультиплікативну дистрибутивність, оскільки після цього нас просять не обробляти результат.

Пари в кожній парі представляють доданки, які ми хочемо помножити, причому перший елемент є коефіцієнтом, а другий - показником. Для множення доданків множимо коефіцієнти і додаємо експоненти разом (ахcбхг=абхcхг=аб(хcхг)=(аб)хc+г). Як ми це робимо? Давайте обробляти другу пару, [[6, 2], [-2, 3]].

Спочатку переносимо пару:

[[6, -2], [2, 3]]

Потім беремо добуток першої пари і суму другої:

[-12, 5]

Відповідна частина коду PSƭ€насправді не скидає лічильник для кожної пари термінів, але, оскільки вони пари, цього не потрібно.

Обробляючи всі пари термінів, ми маємо:

[[6, 4], [-12, 5], [7, 3], [-14, 4], [-2, 2], [4, 3]]

Тут множення зроблено, оскільки нам не потрібно поєднувати подібні терміни. Заключна процедура - «Прекрасна».

Ми спочатку приєднуємося до кожної пари з "x^":

[[6, 'x', '^', 4], [-12, 'x', '^', 5], [7, 'x', '^', 3], [-14, 'x', '^', 4], [-2, 'x', '^', 2], [4, 'x', '^', 3]]

Потім ми приєднуємось до списку за допомогою " + ":

[6, 'x', '^', 4, ' ', '+', ' ', -12, 'x', '^', 5, ' ', '+', ' ', 7, 'x', '^', 3, ' ', '+', ' ', -14, 'x', '^', 4, ' ', '+', ' ', -2, 'x', '^', 2, ' ', '+', ' ', 4, 'x', '^', 3]

Зауважте, як у нас все ще є номери у списку, тож насправді це не рядок. Однак у Jelly є процес під назвою "стрифікація", який запустився в кінці виконання програми для друку результату. Для списку глибини 1 він дійсно просто перетворює кожен елемент на його рядкове представлення та об'єднує рядки разом, тому ми отримуємо потрібний вихід:

6x^4 + -12x^5 + 7x^3 + -14x^4 + -2x^2 + 4x^3

1

JavaScript, 112 110 байт

Я знайшов дві альтернативи однакової довжини. Виклик із синтаксисом currying:f(A)(B)

A=>B=>(P=x=>x.split`+`.map(x=>x.split`x^`))(A).flatMap(a=>P(B).map(b=>a[0]*b[0]+'x^'+(a[1]- -b[1]))).join` + `

A=>B=>(P=x=>x.split`+`.map(x=>x.split`x^`))(A).flatMap(([c,e])=>P(B).map(([C,E])=>c*C+'x^'+(e- -E))).join` + `

-2 байти ( Луїс ): Видаліть пробіли навколо splitроздільника.


JavaScript, 112 байт

Використання String.prototype.matchAll.

A=>B=>(P=x=>[...x.matchAll(/(\S+)x.(\S+)/g)])(A).flatMap(a=>P(B).map(b=>a[1]*b[1]+'x^'+(a[2]- -b[2]))).join` + `


1
split' + ' => split'+'щоб зберегти 2 байти
Luis felipe De


@EmbodimentofIgnorance Мій поганий, я перечитав коментар Луїса. Я подумав, що це було про join.
Арнольд
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.