Страницы

Поиск по вопросам

Показаны сообщения с ярлыком преобразование. Показать все сообщения
Показаны сообщения с ярлыком преобразование. Показать все сообщения

воскресенье, 29 марта 2020 г.

Как преобразовать string в char?

#cpp #строки #char #преобразование


Есть произвольная строка не больше 25 символов. Например "wo1fram"
Как преобразовать ее в массив char[255]?
Чтобы потом с char можно было работать как с полноценным массивом символов, оканчивающимся
нуль-символом.
    


Ответы

Ответ 1



Тут, видите ли, есть два решения. Одно - если вам надо только читать эту строку, или там, поменять в ней пару символов - но не менять ее размер (так что всякие strcpy отменяются) - то можно воспользоваться функциями c_str() и data(). Очень рекомендую внимательно почитать описания, а главное - ограничения, накладываемые этими функциями. И другое - если нужно работать с ней как со строкой в стиле С со всеми возможностями - то просто скопируйте ее в массив, типа char buf[255]; strcpy(buf,s.c_str()); или char * buf = strdup(s); Примерно так.

воскресенье, 15 марта 2020 г.

Транспонировать матрицу, разбив на блоки

#c #матрицы #преобразование


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

Проблема возникла в написании кода самого транспонирования - выполнение затыкается
и ничего не работает.
Ниже сам кусок кода

void transposematrixblocked(int **src, int **dst, int size) {
  for (int i = 0; i < size; i + BLOCKSIZE) {
    for (int j = 0; j < size; j + BLOCKSIZE) {
      for (int ini = 0; ini < BLOCKSIZE; ini ++) {
        for (int inj = 0; inj < BLOCKSIZE; inj ++) {
            dst[i+ini][j+inj] = src[j+inj][i+ini];
        }
      }
    }
  }
}


где я оплошала и как сделать правильно?
    


Ответы

Ответ 1



В цикле for 3-й параметр должен быть вида i += BLOCKSIZE void transposematrixblocked(int **src, int **dst, int size) { for (int i = 0; i < size; i += BLOCKSIZE) { for (int j = 0; j < size; j += BLOCKSIZE) { for (int ini = 0; ini < BLOCKSIZE; ini ++) { for (int inj = 0; inj < BLOCKSIZE; inj ++) { dst[i+ini][j+inj] = src[j+inj][i+ini]; } } } } }

Ответ 2



Основная ошибка действительно была в синтаксисе - i + BLOCKSIZE, вместо i += BLOCKSIZE. Итоговый работающий код ниже: /* Transpose the blocked square matrix src and put the result in dst */ void transposematrixblocked(int **src, int **dst, int size) { for (int i = 0; i < size; i += BLOCKSIZE) { for (int j = 0; j < size; j += BLOCKSIZE) { for (int ini = i; ini < i + BLOCKSIZE; ini ++) { for (int inj = j; inj < j + BLOCKSIZE; inj ++) { dst[ini][inj] = src[inj][ini]; } } } }

четверг, 13 февраля 2020 г.

Преобразование звука

#преобразование #аудио #cpp #linux


Как известно в линукс есть консольные прогарммы aplay и arecord
чтобы проиграть звук с микрофона на колонки нужно ввести:

arecord | aplay

но я хочу добавить эффект дисторшн в эту конструкцию, чтобы было примерно так:
arecord | distort | aplay

Чтобы можно было например на вход подключить электрогитару ну или просто побаловаться.
Так вот мой вопрос вот в чем: Что мне нужно почитать и что изучить, чтобы я мог написать
этот самый distortion?    


Ответы

Ответ 1



Ну, вообще для начала нужно прочитать руководство по командам arecord и aplay. Из него мы узнаем, что по умолчанию они используют формат WAVE. Остается только понять сам формат данных и можно писать утилиту. Также стоит упомянуть, что нужно научиться работать со стандартным вводом и выводом. Но это должно быть достаточно просто.

Ответ 2



#include int main() { int c, i; for ( i = 0; i <= 245; i++) { c = getchar(); printf("%c", c); } while (c != EOF) { c = getchar(); if (c <= 198) printf("%c", 198); else if (c >= 205) printf("%c", 205); else printf("%c", c); } return 0; } это код, который получился у меня. просто сконвертировать файл можно таким образом: ./dist < file1.wav > file2.wav

понедельник, 10 февраля 2020 г.

В переменной типа long не помещается выражение 300*300

#c #arduino #преобразование


В языке ардуино, если писать 

long A = 90000;
Serial.println(A);


то все правильно работает, но если писать

long A = 300*300;
Serial.println(A);


то выводит 24464. И даже если писать 

Serial.println(300*300);


то результат тот же. В чем может быть проблема?
    


Ответы

Ответ 1



Похоже, что выражение long A = 300*300; ^^^^^^^^ вычисляется, как имеющее тип int, и объект типа int не может вместить в себя результирующее значение. Запишите следующим образом long A = ( long )300*300; или long A = 300l*300l; Что касается данной инициализации long A = 90000; то для целочисленного литерала компилятор определяет тот целочисленный тип, который может вместить в себя данное значение. Согласно стандарту C (6.4.4.1 Integer constants) 5 The type of an integer constant is the first of the corresponding list in which its value can be represented И далее в таблице указывается, что когда литерал не имеет суффикса, то последовательно подбирается тип литерала в порядке int, long int, long long int.

среда, 5 февраля 2020 г.

С++ Как работает передача/возврат массивов, в чём разница между int[][] и int**?

#cpp #массивы #указатели #преобразование


Так не компилируется

class A {
private: int arr[10][10];
public: int** getArr() {return arr;}
}


Так собирается, но получаем ошибку во время исполнения(код 11 - попытка доступа к
заблокированной памяти) https://ideone.com/Pq9gLn

class A {
private: int arr[10][10];
public: int** getArr() {return (int**)arr;}
}
...
A a;
int** arr = a.getArr();
cout << arr[0][0];


Почему так? Ведь по идее int** и int[][] одно и то же. В чём разница?
    


Ответы

Ответ 1



Массив в выражениях преобразуется к указателю на свой первый элемент. Если у вас есть, например, объявление массива T a[N]; где T это некоторый тип, а N - число элементов в массиве, то использование имени a в выражениях преобразуется к типу T *. Это можно представить как T *tmp = a; Двумерный массив - это массив массивов. То есть если у вас есть массив вида int a[10][10]; то a - это массив из 10 элементов, которые в свою очередь массивы с типом int[10]. Вы можете ввести объявление typedef для этих элементов. Например, typedef int T[10]; И тогда объявление массива будет выглядеть как T a[10]; Как сказано выше, в выражениях массив преобразуется в указатель на свой первый элемент. Следовательно это преобразование можно представить как T *tmp = a; где T - это алиас для типа int[10] Следовательно, если убрать объявление typedef, то вы получите int ( *tmp )[10] = a; Типы int ( * )[10] и int ** - два разных типа. Например, выведите на консоль размер объектов, для которых определены эти указатели и сравните их #include int main() { int **p; int ( *q )[10]; std::cout << sizeof( *p ) << std::endl; std::cout << sizeof( *q ) << std::endl; return 0; } Вывод программы может выглядеть следующим образом 4 40 То есть в первом случае выводится размер скалярного объекта, а во втором случае размер массива. Поэтому правильное определение метода в вашем классе будет выглядеть так class A { private: int arr[10][10]; public: int ( * getArr() )[10] { return arr; } }; или class A { private: int arr[10][10]; public: typedef int ( *T )[10]; T getArr() { return arr; } }; Что касается вашего примера class A { private: int arr[10][10]; public: int** getArr() {return (int**)arr;} }; ... A a; int** arr = a.getArr(); cout << arr[0][0]; то переменная arr получит адрес экстента, занимаемого исходным двумерным массивом. При использовании выражения arr[0] происходит обращение к памяти массива, где хранится его первый элемент. При этом предполагается, что arr[0] , эквивалентное выражению *arr, в свою очередь вернет указатель. Но исходный массив не хранит указатели. Он хранит в общем случае произвольные значения. Поэтому происходит ошибка обращения к памяти. Для наглядности рассмотрите следующий пример. Допустим, что sizeof( int ) и sizeof( int * ) равны между собой. Чтобы у вас работала конструкция arr[0][0], где arr имеет тип int **, исъодный массив должен быть определен, как показано в следующей демонстрационной программе. #include int main() { int a[][2] = { { reinterpret_cast( &a[1][0] ), 20 }, { 30, 40 }, }; int **arr = reinterpret_cast( a ); std::cout << arr[0][0] << std::endl; return 0; } В этом случае arr[0] возвратит указатель на элемент массива a[1][0], то есть &a[1][0] . Применяя к полученному выражению снова оператор индексирования, вы получите целое число 30. Однако если первый элемент массива содержит произвольное целое число, как, например, 10, то arr[0] вернет это значение, которое в выражении arr[0][0] будет интерпретироваться как адрес памяти, и произойдет ошибка обращения к памяти. Таким образом указатель int **arr; интерпретирует массив int a[N][N]; как массив, имеющий тип int * tmp[N]; То есть рассматривает элементы исходного массива как объекты, хранящие действительные значения указателей, а это в общем случае не так.

Когда вызывается оператор преобразования типов?

#преобразование #cpp


#include 
using namespace std;

class three_d {
    int x, y, z;
public:
    three_d(int a, int b, int c) {x=a; y=b, z=c; }
    three_d operator+(three_d op2);
    friend ostream &operator<<(ostream &stream, three_d &obj);
    operator int() {return x*y*z;}
};
ostream &operator<< (ostream &stream, three_d &obj)
{
    stream << obj.x << ", ";
    stream << obj.y << ", ";
    stream << obj.z << endl;
    return stream;
}
three_d three_d::operator+ (three_d op2)
{
    x+=op2.x;
    y+=op2.y; 
    z+=op2.z; 
    return *this;
}

int main()
{
    three_d a(1, 2, 3), b(2, 3, 4);
    cout << a << b;
    cout << b+100 << endl; //31 line
    cout << a+b << endl; // 32
    system("pause");
    return 0;
}

В 31 строчке объект b приводится к int, потому что справа значение int, но зачем
в 32 строчке привидении работает после того как выполнился operator+? Ведь тут два
объекта с обоих сторон, почему тогда вызывается функция преобразования или как оно
там называется, да ещё и в конце?    


Ответы

Ответ 1



Проблема в том, что ваш объект суммы не lvalue. Для него нужно const: friend ostream &operator<<(ostream &stream, const three_d &obj); Без const cout << a+b трактуется не как operator<<(cout, a+b) // ostream &stream, three_d &obj а как operator<<(cout, (int)(a+b)) // ostream &stream, const int& i Объект, не являющийся lvalue, не может быть использован с не-const-ссылкой. Смотрите, что происходит. Компилятор пытается понять, что же ему вызывать для выражения cout << a + b. Поскольку a + b -- не lvalue, то ostream &operator<< (ostream &stream, three_d &obj) отпадает. Раз так, компилятор смотрит, как он может преобразовать аргументы, чтобы другие функции подошли. Компилятор пробует известные ему операторы <<. Когда он пробует ostream &operator<< (ostream &stream, const int &i), он видит, что можно использовать этот оператор, если преобразовать второй аргумент в int. Поскольку вы предоставили преобразование, этот вариант проходит. Отдельно от темы: ваш оператор сложения -- ужас! Вы модифицируете первое слагаемое! Представьте себе, если бы сложение чисел вело себя так: int a = 5; int b = 7; int c = a + b; // здесь внезапно a == 12 Ваш код ведёт себя именно так. Вот как надо: three_d three_d::operator+ (three_d op2) { return three_d(x + op2.x, y + op2.y, z + op2.z); }

среда, 29 января 2020 г.

Как преобразовать double? в double на C#

#c_sharp #преобразование #типы


Есть поле класса типа double?, то есть оно может и не содержать значения.

А я в своем классе использую просто double.

Как сделать преобразование?

И как быть с DateTime? -> DateTime
    


Ответы

Ответ 1



Если вы уверены, что значение там есть, вы можете получить его так: double? nd = ...; double v = nd.Value; Если не уверены, вам придётся сначала проверить: double? nd = ...; if (nd == null) { // значения нет, обрабатываем этот случай } else { // значение есть double d = nd.Value; // работаем с ним } Для случая, когда для отсутствующего значения подойдёт, например, 0.0, можно написать просто так: double d = nd ?? 0.0; Но как именно правильно реагировать на отсутствующее значение, решать только вам.

Ответ 2



Ну можно что-нибудь вот такое например: double val = nullableDouble.HasValue ? nullableDouble.Value : 0.0;

вторник, 28 января 2020 г.

Как выполнить правильное преобразование строки в дату?

#java #строки #дата #преобразование


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

String dat ="Sat Jan 03 19:47:23 MSK 1984";
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
        simpleDateFormat.applyPattern("EEE MMM dd HH:mm:ss zzz yyyy");
        Date birthDate = simpleDateFormat.parse(dat);


Выкидывает вот такую ошибку:

Exception in thread "main" java.text.ParseException: Unparseable date: "Sat Jan 03
19:47:23 MSK 1984" at java.text.DateFormat.parse(DateFormat.java:366)

    


Ответы

Ответ 1



Все дело в локали: String dat ="Sat Jan 03 19:47:23 MSK 1984"; SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ENGLISH); Date birthDate = simpleDateFormat.parse(dat);

Ответ 2



Попробуйте так: String str = "Sat Jan 03 19:47:23 MSK 1984"; DateFormat format = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ENGLISH); Date date = format.parse(str); Как правильно сказали StateItPrimitive и ЮрийСПб, парсинг даты происходит с учётом локали.

Ответ 3



Кроме вышеперечисленного еще можно добавить, что есть метод ParseExact, который анализирует строку согласно некоторого шаблона

вторник, 7 января 2020 г.

Преобразование типов в C++

#cpp #типы_данных #преобразование


Как в C++ происходит преобразование типов при присвоении беззнаковому типу отрицательного
числа или числа не из диапазона типа? 
    


Ответы

Ответ 1



При выполнении присваивания производится преобразование исходного значения к типу переменной-приемника. Поведение определяется правилами таких преобразований. Переполнение при преобразовании в беззнаковый целый тип из целых типов (как знаковых, так и беззнаковых) обрабатывается по правилами модульной арифметики с модулем 2^N, где N количество значащих бит в целевом беззнаковом типе. Переполнение при преобразовании в знаковый целый тип из целых типов (как знаковых, так и беззнаковых) приводит к поведению, определяемому реализацией. Реализация в том числе имеет право в таких ситуациях выкидывать сигнал. Переполнение при преобразовании в целый тип из плавающих типов приводит к неопределенному поведению. Переполнение при преобразовании в плавающий тип приводит к неопределенному поведению.

Ответ 2



Данные преобразования описаны в разделе 4.7 Integral conversions стандарта C++. В этом разделе в отношении преобразования из знакового целочисленного типа в беззнаковый целочисленный тип написано 2 If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type). [ Note: In a two’s complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). —end note ] Что касается преобразования в знаковый целочисленный тип, то там же написано 3 If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined. Если же объекты знаковых и беззнаковых целых чисел принимают участие в выражении, то сначала определяется их общий тип, чтобы вывести тип значения выражения, согласно правилам обычных арифметических преобразований. Например, согласно этим правилам если два целочисленных типа, беззнаковый и знаковый, имеют одинаковый ранг, то объект знакового типа преобразуется к беззнаковому типу. Вот пример, на который не каждый программист сможет сходу ответить. Допустим у вас на машине sizeof( long ) равняется sizeof( int ). Можно предположить, что оба эти выражения равны 4, хотя это не обязательно. И имеются следующие объявления unsigned int x = 0; long y = 0; Спрашивается: какой тип будет иметь выражение:) x + y

вторник, 31 декабря 2019 г.

Преобразование из префиксной записи в инфиксную

#c_sharp #алгоритм #pascal #преобразование #польская_запись


Как преобразовать префиксную запись (польскую нотацию) в инфиксную. Запись может
содержать: '(', ')', '+', '-', '*', '/', '0..9', 'a..z'. Подскажите алгоритм решения
данной задачи (описание, Pascal, C#), какие структуры данных нужно использовать?
    


Ответы

Ответ 1



Очевидный честный алгоритм — распарсить польскую запись в дерево. Имея дерево, можно рекурсивно строить любые формы записи (а также оптимизировать, компилировать и выполнять, что угодно). Для записи − 5 * 6 7 вы делаете следующие шаги: Видите минус, аллоцируете узел бинарной операции с двумя операндами. Читаете первый операнд. видите 5, это и есть первый операнд, аллоцируете для него листовой узел с константой Читаете второй операнд. видите *, аллоцируете для него узел бинарной операции с двумя операндами читаете первый операнд видите 6, аллоцируете для него листовой узел с константой видите 7, аллоцируете для него листовой узел с константой Для конкретной задачи можно упростить результат, и не строить дерево, а сразу выводить данные. Алгоритм будет такой: Прочитать один токен Если это константа, она и есть результат Если это бинарная операция, запомнить её тип, рекурсивно получить значение операндов, вывести выражение, заключив его в скобки, чтобы не думать о приоритете операторов. дополните правилами для других типов узлов по вкусу

Ответ 2



Если лишние скобки в результате - не проблема, то можно сконвертировать обычной рекурсией: class Program { static void Main(string[] args) { string source = "- * / 15 - 7 + 1 1 3 + 2 + 1 1"; var tokens = new Queue(source.Split()); Console.WriteLine(DoConvert(tokens)); } private static string[] operators = new string[] { "+", "-", "*", "/" }; private static string DoConvert(Queue tokens) { var token = tokens.Dequeue(); if (operators.Contains(token)) { return String.Format("({0} {1} {2})", DoConvert(tokens), token, DoConvert(tokens)); } else { return token; } } }

понедельник, 23 декабря 2019 г.

Возможно ли присвоить результат new не указателю?

#cpp #указатели #конструктор #преобразование


Насколько мне известно, результат выполнения new нужно присваивать указателю, т.е.:

T t = new T(); //должна быть ошибка. Несоответствие типов т.к. new возвращает указатель
T *t = new T(); // Правильный вариант


Однако, необъяснимым для меня образом, следующий код делает первый вариант возможным.
Причем только для одного конечного класса C1. Если попытаться сделать подобное с другим
C2, появляется ошибка. Причем если упростить конструктор (убрать список инициализации
и аргументы), то все будет работать по-обычному.

#include 

class A
{
private:
    int id;
    static int instrumentsCount;
    static int lastId;
public:
    A();
    virtual ~A() = 0;
};
class B:public A
{
private:
    int field1;
public:
    B(int arg):A(),field1(arg) {std::cout<<"B\n";}
};
class C1:public B
{
private:
    const bool field2;
public:
    C1(bool o = true):B(170),field2(o){std::cout<<"C1\n";}
    ~C1();
};
class C2:public B
{
private:
    const int field3;
public:
    C2(int d = 20):B(120),field3(d){std::cout<<"C2\n";}
    ~C2();
};
int A::instrumentsCount = 0;
int A::lastId = 0;
A::A()
{
    this->id = A::lastId++;
    A::instrumentsCount++;
    std::cout<<"A "<id<<" created\n";
}
A::~A()
{
    std::cout<<"A "<id<<" destroyed\n";
    A::instrumentsCount--;
}
C1::~C1(){}
C2::~C2(){}

int main(int argc, char *argv[])
{
    C1 c1 = new C1(true);
    C2 *c2 = new C2(10);
    return 0;
}


Как результат выводится следующее: 

A 0 created
B
C1
A 1 created
B
C1
A 2 created
B
C2
A 1 destroyed


Что в принципе логично, но почему конструктор базового класса A вызывается при создании
C1 два раза? Прошу открыть мне глаза на мои ошибки( Использую QT Creator 5.9
    


Ответы

Ответ 1



Имеется фундаментальный тип, для которого вы можете написать выражение T t = new T(); Таким типом является фундаментальный тип bool. bool b = new bool(); Если инициализатор отличен от нуля, то переменная получает значение true , в противном случае значение false. Из стандарта C++ (4.14 Boolean conversions) 1 A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to a prvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false; any other value is converted to true. For direct-initialization (8.6), a prvalue of type std::nullptr_t can be converted to a prvalue of type bool; the resulting value is false. Однако такой код ведет к утечке памяти, так как значение указателя на выделенную память теряется. В примере кода из вашего вопроса в классе C1 имеется конструктор преобразования C1(bool o = true):B(170),field2(o){std::cout<<"C1\n";} Параметр этого класса имеет тип bool, а переданный в качестве аргумента указатель может неявно быть преобразован в тип bool.

Ответ 2



Да все не просто просто, а очень просто: C1 c1 = new C1(true); Итак, создается новый C1, указатель на который используется как инициализатор для конструирования c1. Есть конструктор C1(bool), который и использован. Имеем - созданный в динамической памяти C1, потерянный (утечка памяти), так как ненулевое значение указателя просто неявно преобразовано в bool для вызова второго конструктора - конструктора, который создает c1... Что логично - вы же создаете два объекта C1.

Ответ 3



Моё дополнение не совсем по теме, но может быть кому-то полезно. Результат, возвращённый оператором new (или функцией malloc) можно присвоить целому числу, но нужно использовать приведение типов: uint32_t a = (uint32_t)malloc(5); Тогда в переменную a попадёт адрес участка памяти, выраженный целым числом. В рядовых программах так делать не рекомендуется, но если вы работаете напрямую с железом, это может быть полезно. Например, это число можно будет записать в регистр контроллера DMA.

четверг, 12 декабря 2019 г.

Преобразовать int в enum

#c_sharp #преобразование #enum


Как можно преобразовать int в enum в C#-e?
    


Ответы

Ответ 1



Из int: CustomEnum enm = (CustomEnum)number; Можно еще и: CustomEnum enm = (CustomEnum)Enum.ToObject(typeof(CustomEnum), number); Из string: CustomEnum enm = (CustomEnum)Enum.Parse(typeof(CustomEnum), str);

Ответ 2



Прежде чем преобразовывать число в перечисление необходимо проверить, принадлежит ли число перечислению, чтобы не выйти за пределы enum и не получить неожиданного поведения кода из-за непредвиденного значения: int number = 1; if (Enum.IsDefined(typeof(CustomEnum), number)) { CustomEnum enm = (CustomEnum)number; // преобразование // или CustomEnum enm = (CustomEnum)Enum.ToObject(typeof(CustomEnum), number); } Документация на MSDN: Enum.IsDefined enum (C# Reference) Enumeration Types (C# Programming Guide) Enum.TryParse Из примера документации на MSDN: using System; [Flags] enum Colors { None=0, Red = 1, Green = 2, Blue = 4 }; public class Example { public static void Main() { string[] colorStrings = { "0", "2", "8", "blue", "Blue", "Yellow", "Red, Green" }; foreach (string colorString in colorStrings) { Colors colorValue; if (Enum.TryParse(colorString, out colorValue)) if (Enum.IsDefined(typeof(Colors), colorValue) | colorValue.ToString().Contains(",")) Console.WriteLine("Converted '{0}' to {1}.", colorString, colorValue.ToString()); else Console.WriteLine("{0} is not an underlying value of the Colors enumeration.", colorString); else Console.WriteLine("{0} is not a member of the Colors enumeration.", colorString); } } } // The example displays the following output: // Converted '0' to None. // Converted '2' to Green. // 8 is not an underlying value of the Colors enumeration. // blue is not a member of the Colors enumeration. // Converted 'Blue' to Blue. // Yellow is not a member of the Colors enumeration. // Converted 'Red, Green' to Red, Green. В примере имеет место проверка Enum.IsDefined. Обезопасить свой код от возможных ошибок - не является признаком плохого тона программирования, я так думаю.

среда, 11 декабря 2019 г.

Умножение больших чисел при помощи дискретного преобразования Фурье

#cpp #алгоритм #преобразование #фурье


Немного теории. Дискретное преобразование Фурье - это линейное преобразование, которое
задается матрицей Вандермонда. Матрица состоит из степеней примитивного корня из единицы
степени n, где n - длина векторов и размерность квадратной матрицы. Проще показать,
как она строится, чем описывать словами.



Матрица обратного преобразования порождается степенями элемента, обратного к выбранному
примитивному корню из единицы. 

Преобразование Фурье над конечным числовым полем почему-то непопулярно у кодеров
в Роиссе, но оно есть и используется в криптографии. Разница в том, что корень из единицы
извлекается не в комплексном поле, а в поле классов вычетов. Остальное все то же самое.

На свёртку можно смотреть как на операцию умножения многочленов с коэффициентами
из поля или конечного кольца классов вычетов. Если применить нормализацию (перекидывать
перенос при переполнении разрядов), то получается умножение длинных чисел. Как я понял,
свёртка и преобразование Фурье достаточно умны, чтобы при умножении чисел расширять
конечное поле, если в нем не хватает цифр. Тут надо сделать важное пояснение примером.

Хотим найти преобразование Фурье от вектора (4, 3, 2, 1) над числовым полем Галуа
GF(5) характеристики 5. Матрица Вандермонда порождается примитивным корнем этого поля,
т.е. двойкой. 

Замечание: не всякий элемент, взаимно простой с модулем, по которому строится поле,
является примитивным корнем. Русская педивикия врет. Например, степени двойки не покроют
все элементы поля GF(7). 

В результате действия этой матрицей на вектор (4, 3, 2, 1) получается вектор (0,
1, 2, 3). Но что, если мы захотим найти преобразование Фурье от вектора (7, 1, 7, 1)?
В поле GF(5) нет элемента 7, но есть класс эквивалентности [2], содержащий семерку.
Как в этом случае отработает преобразование?

Аналогичная ситуация возникнет, если будем умножать числа при помощи этих преобразований.
Например, умножить число 7777 (вектор (7, 7, 7, 7)) на себя. Здесь снова используется
поле GF(5), в котором опять нет семерки.

Свёртка работает так: дополняем два данных вектора нулями (их длина при этом увеличивается
в два раза), применяем к ним прямое преобразование Фурье, два полученных вектора умножаем
почленно и к результату применяем обратное преобразование Фурье. Эта операция работает
как умножение двух многочленов с заданными коэффициентами из поля.

Пример. Найдем свертку (2, 1) и (1, 1). Дополним векторы нулями, получим a' = (2,
1, 0, 0) и b' = (1, 1, 0, 0). Применим к ним прямое преобразование: F(a') = (3, 4,
1, 0) и F(b') = (2, 3, 0, 4). Умножим последние векторы почленно: (1, 2, 0, 0). Применим
к полученному вектору обратное преобразование: (2, 3, 1, 0).

Моя реализация преобразований Фурье, свёртки и умножения длинных чисел:

#include 
#include 

// Возведение a в степень b по модулю p
int powmod (int a, int b, int p)
{
    int res = 1;
    while (b)
        if (b & 1)
            res = int ((long long) res * a % p),  --b;
        else
            a = int ((long long) a * a % p),  b >>= 1;
    return res;
}

// Вычисление обратного к a элемента по модулю n
int inverse(int a, int n)
{
    int b0 = n, t, q;
    int x0 = 0, x1 = 1;
    if (n == 1) return 1;
    while (a > 1) {
        q = a / n;
        t = n, n = a % n, a = t;
        t = x0, x0 = x1 - q * x0, x1 = t;
    }
    if (x1 < 0) x1 += b0;
    return x1;
}

// Вычисление примитивного корня числового поля
// (Стандартный DFT использует комплексные числа, поэтому изменим алгоритм для работы
в Zp)
// Ограничение: p - простое
int generator (int p)
{
    std::vector fact;
    int phi = p - 1,  n = phi;
    for (int i = 2; i * i <= n; ++i)
        if (n % i == 0)
        {
            fact.push_back (i);
            while (n % i == 0)
                n /= i;
        }
    if (n > 1)
        fact.push_back (n);

    for (int res = 2; res <= p; ++res) {
        bool ok = true;
        for (size_t i = 0; i < fact.size() && ok; ++i)
            ok &= powmod (res, phi / fact[i], p) != 1;
        if (ok)  return res;
    }
    return -1;
}

// Реализация прямого DFT над Zp
// Принимает вектор длины n, эта длина используется при вычислении
// примитивного корня поля
// Вычисления приводятся по mod (n + 1), т.к. длина вектора на 1 меньше p,
// где p: Zp
std::vector forward_dft (std::vector& a)
{
    std::vector result;
    int prim_root = generator (a.size());   // Корень степени n из единицы
    for (int i = 0; i < a.size(); i++)
    {
        int sum   = 0;
        int power = 0;
        for (int j = 0; j < a.size(); j++)
        {
            int power_of_root = powmod (prim_root, power, a.size() + 1);
            sum   += power_of_root * a[j];  // Накапливаем сумму произведений элементов
строки матрицы на вектор
            power += i;                     // Увеличиваем степень, в которую возводится
примитивный корень из единицы
        }
        result.push_back(sum % (a.size() + 1)); // Набрали сумму, приводим по mod
p, p = длина вектора + 1
    }
    return result;
}


std::vector inversed_dft (std::vector& a)
{
    std::vector result;
    int prim_root = generator (a.size());   // Корень степени n из единицы
    int inv_prim_root = inverse (prim_root, a.size() + 1);  // Обратный к найденному
примитивному корню
    int inv_n         = inverse (a.size(), a.size() + 1);   // Обратный к n по mod
(n + 1)
    for (int i = 0; i < a.size(); i++)
    {
        int sum   = 0;
        int power = 0;
        for (int j = 0; j < a.size(); j++)
        {
            int power_of_inv_root = powmod (inv_prim_root, power, a.size() + 1);
            sum   += inv_n * power_of_inv_root * a[j];  // Накапливаем сумму произведений
элементов строки матрицы на вектор
            power += i;                         // Увеличиваем степень, в которую
возводится примитивный корень из единицы
        }
        result.push_back(sum % (a.size() + 1)); // Набрали сумму, приводим по mod
p, p = длина вектора + 1
    }
    return result;
}

// Свертка двух векторов при помощи дискретного преобразования Фурье
std::vector convolution (std::vector a, std::vector b)
{
    a.insert(a.end(), a.size(), 0);
    b.insert(b.end(), b.size(), 0);

    a = forward_dft(a);
    b = forward_dft(b);

    std::vector result(a.size(), 0);
    for(int i = 0; i < result.size(); i++)
        result[i] = a[i] * b[i];

    result = inversed_dft(result);

    return result;
}

// Умножение двух больших чисел
std::vector multiply (std::vector a, std::vector b)
{
    std::vector result = convolution(a, b);

    // Нормализация, выполнение переносов
    int carry = 0;
    for (int i = 0; i < result.size(); i++)
    {
        result[i] += carry;
        carry = result[i] / 10;
        result[i] %= 10;
    }

    return result;
}

int main()
{
    int a[] = {1, 2, 1, 2, 1, 2, 1, 2};
    int b[] = {7, 5, 7, 5, 7, 5, 7, 5};

    std::vector u(std::begin(a), std::end(a));
    std::vector v(std::begin(b), std::end(b));

    std::vector result = multiply(u, v);
    for(int i = 0; i < result.size(); i++)
        std::cout << result[i] << " ";

    /*std::vector u(std::begin(a), std::end(a));
    std::vector v(std::begin(b), std::end(b));

    std::vector result = convolution(u, v);
    for(int i = 0; i < result.size(); i++)
        std::cout << result[i] << " ";*/


    /*int a[] = {4, 3, 2, 1};
    std::vector v(std::begin(a), std::end(a));

    std::vector result = forward_dft(v);

    std::cout << "[DEBUG]: Forward DFT" << std::endl;
    for(int i = 0; i < result.size(); i++)
        std::cout << result[i] << " ";
    std::cout << std::endl;

    result = inversed_dft(result);

    std::cout << "[DEBUG]: Inversed DFT" << std::endl;
    for(int i = 0; i < result.size(); i++)
        std::cout << result[i] << " ";*/
}


Умножение длинных чисел в функции main работает неправильно. Сначала я думал, что
это связано с тем, что алгоритм работает над конечным полем, в котором нет некоторых
"цифр" большого числа. Но видно, что при умножении чисел 12121212 на 75757575 используется
поле достаточного размера, чтобы содержать там соответствие для цифры 7. При возведении
числа 12 в квадрат алгоритм работает почти правильно, т.е. получается вектор (1, 4,
4, 0), но возникает лишний ноль из-за размерности векторов и дополнения нулями. Где
я ошибся?
    


Ответы

Ответ 1



У вас в программе вообще отсутствует поиск простого числа, в поле которого дальше идёт дискретное преобразование Фурье - это довольно странно. Я предположил, что вы считаете, что размер массива плюс единица является простым числом (что в вашем примере действительно так - 2*8+1=17 - простое). Далее у вас неконсистентно используется функция generator - объявлена она с параметром-простым числом p, а вызывается от a.size() (что, по моему предположению, является p-1). При этом, что забавно, первообразный корень тоже находится - он существует по в том числе по модулям, являющимися степенями двойки. Последняя и фатальная проблема заключается в том, что модуля 17 недостаточно для умножения чисел длины 8 по основанию 10. При умножении чисел мы переходим к умножению многочленов (например, 4321 переходит в многочлен 4x^3+3*x^2+2*x+1), потом умножаем многочлены в поле, а потом превращаем многочлен обратно в число. Если в процессе умножения хотя бы один коэффициент многочлена "переполнился" (т.е. стал больше или равен модуля), то мы всё получим корректный результат умножения многочленов, но он уже не будет соответствовать корректному числу. Например, умножая 4321 на 321 (т.е. (4x^3+3*x^2+2*x+1)*(3*x^2+2*x+1)) мы получим многочлен 12*x^5+17*x^4+16*x^3+10*x^2+4*x+1, что при подстановке x=10 даёт как раз число 1387041. Однако если известны коэффициенты многочлена лишь по модулю 17, то коэффициент при x^4 зануляется и становится невозможно восстановить, чему он был равен на самом деле. Вам следует выбирать поле достаточно большого размера, чтобы при перемножении двух многочленов коэффициенты результата не превосходили модуль.

вторник, 4 июня 2019 г.

Транспонировать матрицу, разбив на блоки

Требуется ускорить транспонирование большой матрицы, элементы размещены в памяти последовательно. Ускорить нужно за счет обработки матрицы блоками, чтобы из кэша необходимые куски памяти не успевали стираться.
Проблема возникла в написании кода самого транспонирования - выполнение затыкается и ничего не работает. Ниже сам кусок кода
void transposematrixblocked(int **src, int **dst, int size) { for (int i = 0; i < size; i + BLOCKSIZE) { for (int j = 0; j < size; j + BLOCKSIZE) { for (int ini = 0; ini < BLOCKSIZE; ini ++) { for (int inj = 0; inj < BLOCKSIZE; inj ++) { dst[i+ini][j+inj] = src[j+inj][i+ini]; } } } } }
где я оплошала и как сделать правильно?


Ответ

В цикле for 3-й параметр должен быть вида i += BLOCKSIZE
void transposematrixblocked(int **src, int **dst, int size) { for (int i = 0; i < size; i += BLOCKSIZE) { for (int j = 0; j < size; j += BLOCKSIZE) { for (int ini = 0; ini < BLOCKSIZE; ini ++) { for (int inj = 0; inj < BLOCKSIZE; inj ++) { dst[i+ini][j+inj] = src[j+inj][i+ini]; } } } } }

суббота, 27 апреля 2019 г.

В переменной типа long не помещается выражение 300*300

В языке ардуино, если писать
long A = 90000; Serial.println(A);
то все правильно работает, но если писать
long A = 300*300; Serial.println(A);
то выводит 24464. И даже если писать
Serial.println(300*300);
то результат тот же. В чем может быть проблема?


Ответ

Похоже, что выражение
long A = 300*300; ^^^^^^^^
вычисляется, как имеющее тип int, и объект типа int не может вместить в себя результирующее значение.
Запишите следующим образом
long A = ( long )300*300;
или
long A = 300l*300l;
Что касается данной инициализации
long A = 90000;
то для целочисленного литерала компилятор определяет тот целочисленный тип, который может вместить в себя данное значение.
Согласно стандарту C (6.4.4.1 Integer constants)
5 The type of an integer constant is the first of the corresponding list in which its value can be represented
И далее в таблице указывается, что когда литерал не имеет суффикса, то последовательно подбирается тип литерала в порядке int, long int, long long int

среда, 10 апреля 2019 г.

С++ Как работает передача/возврат массивов, в чём разница между int[][] и int**?

Так не компилируется
class A { private: int arr[10][10]; public: int** getArr() {return arr;} }
Так собирается, но получаем ошибку во время исполнения(код 11 - попытка доступа к заблокированной памяти) https://ideone.com/Pq9gLn
class A { private: int arr[10][10]; public: int** getArr() {return (int**)arr;} } ... A a; int** arr = a.getArr(); cout << arr[0][0];
Почему так? Ведь по идее int** и int[][] одно и то же. В чём разница?


Ответ

Массив в выражениях преобразуется к указателю на свой первый элемент.
Если у вас есть, например, объявление массива
T a[N];
где T это некоторый тип, а N - число элементов в массиве, то использование имени a в выражениях преобразуется к типу T *. Это можно представить как
T *tmp = a;
Двумерный массив - это массив массивов. То есть если у вас есть массив вида
int a[10][10];
то a - это массив из 10 элементов, которые в свою очередь массивы с типом int[10]
Вы можете ввести объявление typedef для этих элементов.
Например,
typedef int T[10];
И тогда объявление массива будет выглядеть как
T a[10];
Как сказано выше, в выражениях массив преобразуется в указатель на свой первый элемент.
Следовательно это преобразование можно представить как
T *tmp = a;
где T - это алиас для типа int[10] Следовательно, если убрать объявление typedef, то вы получите
int ( *tmp )[10] = a;
Типы int ( * )[10] и int ** - два разных типа.
Например, выведите на консоль размер объектов, для которых определены эти указатели и сравните их
#include
int main() { int **p; int ( *q )[10];
std::cout << sizeof( *p ) << std::endl; std::cout << sizeof( *q ) << std::endl;
return 0; }
Вывод программы может выглядеть следующим образом
4 40
То есть в первом случае выводится размер скалярного объекта, а во втором случае размер массива.
Поэтому правильное определение метода в вашем классе будет выглядеть так
class A { private: int arr[10][10];
public: int ( * getArr() )[10] { return arr; } };
или
class A { private: int arr[10][10];
public: typedef int ( *T )[10]; T getArr() { return arr; } };
Что касается вашего примера
class A { private: int arr[10][10]; public: int** getArr() {return (int**)arr;} }; ... A a; int** arr = a.getArr(); cout << arr[0][0];
то переменная arr получит адрес экстента, занимаемого исходным двумерным массивом. При использовании выражения arr[0] происходит обращение к памяти массива, где хранится его первый элемент. При этом предполагается, что arr[0] , эквивалентное выражению *arr, в свою очередь вернет указатель. Но исходный массив не хранит указатели. Он хранит в общем случае произвольные значения. Поэтому происходит ошибка обращения к памяти.
Для наглядности рассмотрите следующий пример. Допустим, что sizeof( int ) и sizeof( int * ) равны между собой. Чтобы у вас работала конструкция arr[0][0], где arr имеет тип int **, исъодный массив должен быть определен, как показано в следующей демонстрационной программе.
#include
int main() { int a[][2] = { { reinterpret_cast( &a[1][0] ), 20 }, { 30, 40 }, };
int **arr = reinterpret_cast( a );
std::cout << arr[0][0] << std::endl;
return 0; }
В этом случае arr[0] возвратит указатель на элемент массива a[1][0], то есть &a[1][0] . Применяя к полученному выражению снова оператор индексирования, вы получите целое число 30
Однако если первый элемент массива содержит произвольное целое число, как, например, 10, то arr[0] вернет это значение, которое в выражении arr[0][0] будет интерпретироваться как адрес памяти, и произойдет ошибка обращения к памяти.
Таким образом указатель
int **arr;
интерпретирует массив
int a[N][N];
как массив, имеющий тип
int * tmp[N];
То есть рассматривает элементы исходного массива как объекты, хранящие действительные значения указателей, а это в общем случае не так.

воскресенье, 7 апреля 2019 г.

Когда вызывается оператор преобразования типов?

#include using namespace std;
class three_d { int x, y, z; public: three_d(int a, int b, int c) {x=a; y=b, z=c; } three_d operator+(three_d op2); friend ostream &operator<<(ostream &stream, three_d &obj); operator int() {return x*y*z;} }; ostream &operator<< (ostream &stream, three_d &obj) { stream << obj.x << ", "; stream << obj.y << ", "; stream << obj.z << endl; return stream; } three_d three_d::operator+ (three_d op2) { x+=op2.x; y+=op2.y; z+=op2.z; return *this; }
int main() { three_d a(1, 2, 3), b(2, 3, 4); cout << a << b; cout << b+100 << endl; //31 line cout << a+b << endl; // 32 system("pause"); return 0; } В 31 строчке объект b приводится к int, потому что справа значение int, но зачем в 32 строчке привидении работает после того как выполнился operator+? Ведь тут два объекта с обоих сторон, почему тогда вызывается функция преобразования или как оно там называется, да ещё и в конце?


Ответ

Проблема в том, что ваш объект суммы не lvalue. Для него нужно const
friend ostream &operator<<(ostream &stream, const three_d &obj);
Без const cout << a+b трактуется не как
operator<<(cout, a+b) // ostream &stream, three_d &obj
а как
operator<<(cout, (int)(a+b)) // ostream &stream, const int& i
Объект, не являющийся lvalue, не может быть использован с не-const-ссылкой.

Смотрите, что происходит.
Компилятор пытается понять, что же ему вызывать для выражения cout << a + b
Поскольку a + b -- не lvalue, то ostream &operator<< (ostream &stream, three_d &obj) отпадает. Раз так, компилятор смотрит, как он может преобразовать аргументы, чтобы другие функции подошли.
Компилятор пробует известные ему операторы <<. Когда он пробует ostream &operator<< (ostream &stream, const int &i), он видит, что можно использовать этот оператор, если преобразовать второй аргумент в int. Поскольку вы предоставили преобразование, этот вариант проходит.

Отдельно от темы: ваш оператор сложения -- ужас! Вы модифицируете первое слагаемое!
Представьте себе, если бы сложение чисел вело себя так:
int a = 5; int b = 7; int c = a + b; // здесь внезапно a == 12
Ваш код ведёт себя именно так.
Вот как надо:
three_d three_d::operator+ (three_d op2) { return three_d(x + op2.x, y + op2.y, z + op2.z); }

суббота, 30 марта 2019 г.

Как преобразовать double? в double на C#

Есть поле класса типа double?, то есть оно может и не содержать значения.
А я в своем классе использую просто double
Как сделать преобразование?
И как быть с DateTime? -> DateTime


Ответ

Если вы уверены, что значение там есть, вы можете получить его так:
double? nd = ...; double v = nd.Value;
Если не уверены, вам придётся сначала проверить:
double? nd = ...; if (nd == null) { // значения нет, обрабатываем этот случай } else { // значение есть double d = nd.Value; // работаем с ним }
Для случая, когда для отсутствующего значения подойдёт, например, 0.0, можно написать просто так:
double d = nd ?? 0.0;
Но как именно правильно реагировать на отсутствующее значение, решать только вам.

среда, 27 марта 2019 г.

Как выполнить правильное преобразование строки в дату?

Добрый день,читаю из файла строку следующего вида,пытаюсь преобразовать в тип Date. Не получается. Подскажите, пожалуйста, в чем ошибка.
String dat ="Sat Jan 03 19:47:23 MSK 1984"; SimpleDateFormat simpleDateFormat = new SimpleDateFormat(); simpleDateFormat.applyPattern("EEE MMM dd HH:mm:ss zzz yyyy"); Date birthDate = simpleDateFormat.parse(dat);
Выкидывает вот такую ошибку:
Exception in thread "main" java.text.ParseException: Unparseable date: "Sat Jan 03 19:47:23 MSK 1984" at java.text.DateFormat.parse(DateFormat.java:366)


Ответ

Все дело в локали
String dat ="Sat Jan 03 19:47:23 MSK 1984"; SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ENGLISH); Date birthDate = simpleDateFormat.parse(dat);

вторник, 20 ноября 2018 г.

Возможно ли присвоить результат new не указателю?

Насколько мне известно, результат выполнения new нужно присваивать указателю, т.е.:
T t = new T(); //должна быть ошибка. Несоответствие типов т.к. new возвращает указатель T *t = new T(); // Правильный вариант
Однако, необъяснимым для меня образом, следующий код делает первый вариант возможным. Причем только для одного конечного класса C1. Если попытаться сделать подобное с другим C2, появляется ошибка. Причем если упростить конструктор (убрать список инициализации и аргументы), то все будет работать по-обычному.
#include
class A { private: int id; static int instrumentsCount; static int lastId; public: A(); virtual ~A() = 0; }; class B:public A { private: int field1; public: B(int arg):A(),field1(arg) {std::cout<<"B
";} }; class C1:public B { private: const bool field2; public: C1(bool o = true):B(170),field2(o){std::cout<<"C1
";} ~C1(); }; class C2:public B { private: const int field3; public: C2(int d = 20):B(120),field3(d){std::cout<<"C2
";} ~C2(); }; int A::instrumentsCount = 0; int A::lastId = 0; A::A() { this->id = A::lastId++; A::instrumentsCount++; std::cout<<"A "<id<<" created
"; } A::~A() { std::cout<<"A "<id<<" destroyed
"; A::instrumentsCount--; } C1::~C1(){} C2::~C2(){}
int main(int argc, char *argv[]) { C1 c1 = new C1(true); C2 *c2 = new C2(10); return 0; }
Как результат выводится следующее:
A 0 created B C1 A 1 created B C1 A 2 created B C2 A 1 destroyed
Что в принципе логично, но почему конструктор базового класса A вызывается при создании C1 два раза? Прошу открыть мне глаза на мои ошибки( Использую QT Creator 5.9


Ответ

Имеется фундаментальный тип, для которого вы можете написать выражение
T t = new T();
Таким типом является фундаментальный тип bool.
bool b = new bool();
Если инициализатор отличен от нуля, то переменная получает значение true , в противном случае значение false
Из стандарта C++ (4.14 Boolean conversions)
1 A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to a prvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false; any other value is converted to true. For direct-initialization (8.6), a prvalue of type std::nullptr_t can be converted to a prvalue of type bool; the resulting value is false.
Однако такой код ведет к утечке памяти, так как значение указателя на выделенную память теряется.
В примере кода из вашего вопроса в классе C1 имеется конструктор преобразования
C1(bool o = true):B(170),field2(o){std::cout<<"C1
";}
Параметр этого класса имеет тип bool, а переданный в качестве аргумента указатель может неявно быть преобразован в тип bool