Introduction to Dirichlet convolution
Last Updated :
25 Jan, 2023
Dirichlet convolution is a mathematical operation that combines two arithmetic functions to create a third function. It is named after the mathematician Peter Gustav Lejeune Dirichlet and is closely related to the concept of convolution in signal processing.
In mathematics, an arithmetic function is a function that takes positive integers as input and returns a number as output. The most well-known example of an arithmetic function is the divisor function, which counts the number of positive divisors a given number has.
The Dirichlet convolution of two arithmetic functions f and g is denoted by (f * g) and is defined as:
(f * g)(n) = ∑f(d) * g(n/d)
where the sum is taken over all positive divisors d of n.
Examples:
One example of a Dirichlet convolution is the convolution of the divisor function with itself, denoted by (φ * φ). This results in the Euler totient function, which is defined as:
φ(n) = ∑φ(d) * φ(n/d)
where φ(n) is the number of positive integers less than n that are relatively prime to n.
Another example of a Dirichlet convolution is the convolution of the divisor function with the function that returns 1 for all input values. This results in the identity function, which is defined as:
id(n) = ∑φ(d) * 1(n/d)
where id(n) is the function that returns n for all input values.
- Dirichlet convolution can be used to calculate various arithmetic functions, including the number of divisors and the sum of a given number’s divisors.
- It can also be used to find the inverse of an arithmetic function, which is a function that undoes the operation of the original function.
Approaches:
There are several approaches to implementing Dirichlet convolution, depending on the specific requirements of the application.
One simple approach is to use a brute-force method, which involves iterating over all positive divisors of the input value and summing the results of the arithmetic functions at each divisor. This approach has a time complexity of O(n), where n is the input value.
A more efficient approach is to use a sieve algorithm, which pre-calculates the values of the arithmetic functions for all integers up to a certain maximum value and stores them in an array. The Dirichlet convolution can then be calculated by simply multiplying and summing the values in the array. This approach has a time complexity of O(max), where max is the maximum value for which the arithmetic functions are pre-calculated.
Implementation of the Dirichlet convolution in Python:
C++14
#include<bits/stdc++.h>
using namespace std;
int gcd( int a, int b)
{
while (b != 0) {
int temp = b;
b = a % b;
a = temp;
}
return a;
}
int eulerTotient( int n)
{
int result = 0;
for ( int i = 1; i <= n; i++)
{
if (gcd(i, n) == 1) {
result++;
}
}
return result;
}
int divisorFunction( int n)
{
int result = 0;
for ( int d = 1; d <= n; d++)
{
if (n % d == 0) {
result++;
}
}
return result;
}
int dirichletConvolution( int n)
{
int result = 0;
for ( int d = 1; d <= n; d++)
{
if (n % d == 0) {
result += divisorFunction(d) * eulerTotient(n / d);
}
}
return result;
}
int main(){
for ( int i = 1; i <= 10; i++) {
cout<< "φ(" << i << ") = " << eulerTotient(i)<<endl;
}
}
|
Java
import java.io.*;
class GFG {
public static int divisorFunction( int n)
{
int result = 0 ;
for ( int d = 1 ; d <= n; d++)
{
if (n % d == 0 ) {
result++;
}
}
return result;
}
public static int dirichletConvolution( int n)
{
int result = 0 ;
for ( int d = 1 ; d <= n; d++)
{
if (n % d == 0 ) {
result += divisorFunction(d)
* eulerTotient(n / d);
}
}
return result;
}
public static int gcd( int a, int b)
{
while (b != 0 ) {
int temp = b;
b = a % b;
a = temp;
}
return a;
}
public static int eulerTotient( int n)
{
int result = 0 ;
for ( int i = 1 ; i <= n; i++)
{
if (gcd(i, n) == 1 ) {
result++;
}
}
return result;
}
public static void main(String[] args)
{
for ( int i = 1 ; i <= 10 ; i++) {
System.out.println( "φ(" + i
+ ") = " + eulerTotient(i));
}
}
}
|
Python3
def divisor_function(n):
result = 0
for d in range ( 1 , n + 1 ):
if n % d = = 0 :
result + = 1
return result
def dirichlet_convolution(f, g, n):
result = 0
for d in range ( 1 , n + 1 ):
if n % d = = 0 :
result + = f(d) * g(n / / d)
return result
def gcd(a, b):
while b ! = 0 :
a, b = b, a % b
return a
def euler_totient(n):
result = 0
for i in range ( 1 , n + 1 ):
if gcd(i, n) = = 1 :
result + = 1
return result
for i in range ( 1 , 11 ):
print (f "φ({i}) = {euler_totient(i)}" )
|
C#
using System;
public class GFG {
public static int divisorFunction( int n)
{
int result = 0;
for ( int d = 1; d <= n; d++) {
if (n % d == 0) {
result++;
}
}
return result;
}
public static int dirichletConvolution( int n)
{
int result = 0;
for ( int d = 1; d <= n; d++) {
if (n % d == 0) {
result += divisorFunction(d)
* eulerTotient(n / d);
}
}
return result;
}
public static int gcd( int a, int b)
{
while (b != 0) {
int temp = b;
b = a % b;
a = temp;
}
return a;
}
public static int eulerTotient( int n)
{
int result = 0;
for ( int i = 1; i <= n; i++) {
if (gcd(i, n) == 1) {
result++;
}
}
return result;
}
static public void Main()
{
for ( int i = 1; i <= 10; i++) {
Console.WriteLine( "φ(" + i
+ ") = " + eulerTotient(i));
}
}
}
|
Javascript
<script>
function divisorFunction(n) {
let result = 0;
for (let d = 1; d <= n; d++) {
if (n % d === 0) {
result++;
}
}
return result;
}
function dirichletConvolution(f, g, n) {
let result = 0;
for (let d = 1; d <= n; d++) {
if (n % d === 0) {
result += f(d) * g(Math.floor(n / d));
}
}
return result;
}
function gcd(a, b) {
while (b !== 0) {
[a, b] = [b, a % b];
}
return a;
}
function eulerTotient(n) {
let result = 0;
for (let i = 1; i <= n; i++) {
if (gcd(i, n) === 1) {
result++;
}
}
return result;
}
for (let i = 1; i <= 10; i++) {
document.write(`φ(${i}) = ${eulerTotient(i)} <br>`);
}
</script>
|
Output
φ(1) = 1
φ(2) = 1
φ(3) = 2
φ(4) = 2
φ(5) = 4
φ(6) = 2
φ(7) = 6
φ(8) = 4
φ(9) = 6
φ(10) = 4
Complexity analysis:
Time Complexity: In terms of complexity analysis, the time complexity of Dirichlet convolution depends on the specific implementation used. As mentioned earlier, the brute-force method has a time complexity of O(n), while the sieve algorithm has a time complexity of O(max).
Auxiliary Space: The space complexity of both approaches is O(n), as the values of the arithmetic functions must be stored for all positive integers up to the input value.
Optimization technique:
An optimization technique that can be used to improve the performance of Dirichlet convolution is memoization, which involves storing the results of the arithmetic functions in a cache so that they can be quickly retrieved when needed. This can greatly reduce the time required to perform the convolution, especially for large input values.
Share your thoughts in the comments
Please Login to comment...