# Native Code > برنامه نویسی در Delphi > مباحث عمومی دلفی و پاسکال > مقاله: دلفی و ریاضی

## یوسف زالی

سلام.
خیلی دیدم که دوستان در موارد محاسباتی و ریاضی ضعف داشتند. خب، من نمی گم اومدم تا ضعف کسی رو برطرف کنم، سعی دارم اگر وقت کردم و دستم رسید هر از چند گاهی چیزهایی رو که به نظرم جالب می رسه و نکات آموزنده ای داره در قالب یک مقاله شرحه شرحه (!!) برا دوستان بگذارم.
امیدوارم که جالب و مفید باشند و عزیزان استقبال کنند.
لینک مطالب رو در همین نوشته ی اولم می گذارم تا دسترسیش آسون تر باشه.

- Min و Max دو عدد بدون استفاده از شرط
- Swap کردن دو متغیر بدون استفاده از متغیر موقتی
- انتخاب یک بلاک کد بدون استفاده از If
- محاسبه ی دترمینان یک ماتریس با هر اندازه ای
- محاسبه روي سري ها
- رسم یک تابع روی محور های دکارتی
- رسم یک تابع روی محور های قطبی
- استفاده از ریشه یابی در حل معکوس توابعی که سورس تبدیل آن را در اختیار نداریم
- محاسبه تابع فیبوناتچی بدون داشتن جملات قبل
- محاسبه توابع فاکتوریل و گاما برای اعداد اعشاری
- استفاده از قوانین منطقی در برنامه نویسی
- کاربرد عملگر های بیتی در برنامه نویسی
- بسط تیلور توابع سینوس و کسینوس
- محاسبات پرتابه در بازی Angry Birds
- بسط تیلور توابع Ln و exp به روش بهینه
- به توان رساندن همه ی اعداد از جمله اعداد منفی
- به دست آوردن تعداد ارقام در مبنای خاص
- ساخت n عدد تصادفی بدون تکرار
- محاسبه میانگین لحظه ای و اعداد بزرگ (میانگین به فرم بازگشتی)- شبیه سازی بیلیارد (برخورد بین چندین توپ)
- تابعی برای ارزیابی عبارات ریاضی

----------


## یوسف زالی

- Min و Max دو عدد بدون استفاده از شرط


var
  a, b: integer;
  min, max: integer;
begin
  a := 400;
  b := 200;

  min := (a + b - abs(a - b)) div 2;
  max := (a + b + abs(a - b)) div 2;

  ShowMessage('min = ' + IntToStr(min) + ', max = ' + IntToStr(max));
end;


با استفاده از این قطعه کد می تونید بدون استفاده از If مین و ماکس دو عدد رو داشته باشید.
(استفاده کردن یا نکردن از دستورات شرطی در مبحث قفل نویسی خیلی مهمه)

----------


## یوسف زالی

- Swap کردن دو متغیر بدون استفاده از متغیر موقتی

برای جابجا کردن مقادیر دو متغیر معمولا از متغیر سومی به این شکل استفاده می شه:

temp := a;
a := b;
b := temp;


اما با محاسبات ریاضی می تونید بدون اون هم عمل کنید!

جابجایی دو عدد:

var
  a, b: Integer;
begin
  a := 100;
  b := 200;

  a := a + b;
  b := a - b;
  a := a - b;

  ShowMessage('a = ' + IntToStr(a) + ', b = ' + IntToStr(b));
end;


اگر کاراکتر بود چی؟!

var
  a, b: char;
begin
  a := 'A';
  b := 'B';

  a := chr( ord(a) + ord(b) );
  b := chr( ord(a) - ord(b) );
  a := chr( ord(a) - ord(b) );

  ShowMessage('a = ' + a + ', b = ' + b);
end;


خب اگر رشته بود؟

var
  a, b: string;
begin
  a := 'salam';
  b := 'test';

  integer(a) := integer(a) + integer(b);
  integer(b) := integer(a) - integer(b);
  integer(a) := integer(a) - integer(b);

  ShowMessage('a = ' + a + ', b = ' + b);
end;


و البته اگر شی بود!

  integer(Button1) := integer(Button1) + integer(Button2);
  integer(Button2) := integer(Button1) - integer(Button2);
  integer(Button1) := integer(Button1) - integer(Button2);

  ShowMessage('Button1 = ' + Button1.Caption + ', Button2 = ' + Button2.Caption);


 :چشمک:

----------


## یوسف زالی

- انتخاب یک بلاک کد بدون استفاده از If

با استفاده از این قطعه کد می تونید یک شرط رو روی بلاکی از کد اعمال کنید بدون اینکه از دستورات شرطی استفاده کنید.

type
  ProcType = procedure;

procedure P1;
begin
  ShowMessage('Here is P1');
end;

procedure P2;
begin
  ShowMessage('Here is P2');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  b: boolean;
  p: Pointer;
begin
  b := true; // or false or any other boolean expression

  p := pointer(integer(b) * integer(@P1) + (1 - integer(b)) * integer(@P2));

  ProcType(p);
end;


مورد استفاده ی این کار اینه که کرکر رو یک کم سرکار می گذاره. می تونه موارد مصرف جالب دیگه ای هم براش وجود داشته باشه.

----------


## یوسف زالی

- محاسبه ی دترمینان یک ماتریس با هر اندازه ای

دوره ی دانشجویی ماتریس Sparse رو نوشته بودم و حالا ساده شده ی همون رو براتون می گذارم.
فرمولی که استفاده شده یک فرمول بازگشتی من درآوردی هست! البته بعد از زیر و رو کردن اینترنت فهمیدم که به فرمول درستی رسیدم:

Determinant( Matrix[n x n] ) = Sigma[i from 1 to n] { -1^(i +1) * Value[1, i] * Determinant( SubMatrixFrom[1, i] ) }


کد برنامه رو براتون می گذارم.
امیدوارم روزی به کار کسی بیاد و ما رو هم دعا کنه!


type
  TMatrix = array of array of record
    Value: real;
    Flag: integer;
  end;

function GetMatSize(Matrix: TMatrix): integer;
var
  i, j: integer;
begin
  Result := 0;

  for i := 0 to length(Matrix) -1 do
    if Result = 0 then
      for j := 0 to length(Matrix) -1 do
        if Matrix[i, j].Flag = 0 then
          inc(Result);
end;

function Determinant(Matrix: TMatrix; yid: integer = 0): real;
var
  i, j, k, sign: integer;
begin
  Result := 0;

  if GetMatSize(Matrix) = 1 then
    begin
    for i := 0 to length(Matrix) -1 do
      if Matrix[yid, i].Flag = 0 then
        Break;

    Result := Matrix[yid, i].Value;
    Exit;
    end;

  sign := 1;

  for k := 0 to length(Matrix) -1 do
    Matrix[yid, k].Flag := Matrix[yid, k].Flag +1;

  for j := 0 to length(Matrix) -1 do
    if Matrix[yid, j].Flag = 1 then
      begin
      for k := 0 to length(Matrix) -1 do
        Matrix[k, j].Flag := Matrix[k, j].Flag +1;

      Result := Result + sign * Matrix[yid, j].Value * Determinant(Matrix, yid +1);
      sign := -sign;

      for k := 0 to length(Matrix) -1 do
        Matrix[k, j].Flag := Matrix[k, j].Flag -1;
      end;

  for k := 0 to length(Matrix) -1 do
    Matrix[yid, k].Flag := Matrix[yid, k].Flag -1;
end;


مثال:

procedure TForm1.Button1Click(Sender: TObject);
var
  n, i, j: integer;
  Mat: TMatrix;
  det: real;
begin
  n := 3;

  SetLength(Mat, n);
  for i := 0 to length(Mat) -1 do
    begin
    SetLength(Mat[i], n);

    for j := 0 to length(Mat[i]) -1 do
      Mat[i, j].Flag := 0;
    end;

  Mat[0, 0].Value := 1;
  Mat[0, 1].Value := 0;
  Mat[0, 2].Value := 3;

  Mat[1, 0].Value := 4;
  Mat[1, 1].Value := 5;
  Mat[1, 2].Value := 0;

  Mat[2, 0].Value := 7;
  Mat[2, 1].Value := 0;
  Mat[2, 2].Value := 9;

  det := Determinant(Mat);

  ShowMessage(Format('Determinant is %0.3f', [det]));
end;

----------


## یوسف زالی

- محاسبه روي سري ها

خب، قبلش يکم در مورد به توان رسوندن داستان بگم، به درد مي خوره.

قبلا ها که امکانات نبود به توان رسوندن دو تا عدد معضلي شده بود. براي همين از روشهاي جالبي استفاده مي کردن که چند تاشون رو توضيح مي دم.
- به توان رسوندن عدد هاي طبيعي: با حلقه ها انجام مي شه..
- رسوندن عدد حقيقي به توان عدد حقيقي: با استفاده از لگاريتم انجام مي شه:

A^B = 10^(B * Log(A))

که در دلفي مي شه:

Exp(B * Ln(A))

- به توان رسوندن منفي يک: چون فقط علامت يک عوض مي شه تست مي کردن که توان زوج هست يا فرد، بعد منفي يا مثبت يک برمي گشت.

خب، بگذريم،
سري يعني يک تعداد عدد که قانون مشخصي رو دنبال مي کنند.
مثلا سري اعداد طبيعي، سري فيبوناتچي، تايلور، فوريه، ...

قدم اول براي محاسبه روي سري درآوردن فرمولي به شکل تک گزاره ی حاصل از ایندکس هست.
به عنوان مثال اعداد زير رو در نظر بگيريد:

5pi -10pi + 15pi -20pi .. -100pi

اينجا فرمول ما به شکل زير مي شه:

-1^(i +1) * 5 * i * pi   [i from 1 to 20]

که به دلیل اینکه جملات دارن با هم جمع می شن:

Sigma[i from 1 to 20] {-1^(i +1) * 5 * i * pi}


حالا این فرمول به یک For تبدیل می شه که حد بالا و پایینش 1 و 20 هستند.

var
  S: real;
  sign: integer;
  i: integer;
begin
  S := 0; // cause of sum, in multiply case use 1
  sign := 1;

  for i := 1 to 20 do
    begin
    S := S + sign * 5 * i * pi;
    sign := -sign;
    end;

  ShowMessage(FloatToStr(S));
end;

در اینجا به توان رسوندن منفی یک رو دقت کنید.
چون علامت در هر جمله عوض می شه از یک متغیر براش استفاده شده.

تقریبا همه ی سری ها رو به همین راحتی می شه روشون محاسبه انجام داد. من تقریبا همه ی تمرین های محاسبات عددیم رو با برنامه نویسی حل می کردم!
 :شیطان:

----------


## یوسف زالی

- رسم یک تابع روی محور های دکارتی

خیلی پیش میاد که بخواهیم تو کارهای تحقیقاتی یا برنامه های گرافیکی تحلیلی یک نمودار از تابع خاصی بسازیم.
منظور من از نمودار مثلا میله ای یا کیکی نیست، منظور نمودار های ریاضی هستند که توابعی رو Plot می کنند.
قطعه کدی که ارائه می شه هسته ی این جور کارهاست که شما می تونید با توجه به نیازتون اصلاحش کنید.
می تونید یک Parser کوچیک بهش اضافه کنید تا از یک ادیت تابع رو از یوزر بگیره، یا برای اون قسمت می تونید از PascalScript کمک بگیرید.

کد:

type
  TFuncForPlot = function(x: real): real;

function Fx1(x: real): real;
begin
  Result := sin(x);
end;

function Fx2(x: real): real;
begin
  Result := x / exp(x*x);
end;

procedure Plot(ACanvas: TCanvas; AWidth, AHeight: integer; AFunction: TFuncForPlot; Scale: integer; AColor: TColor);
var
  pos: real;
  y: real;
  lastPoint, thisPoint: TPoint;
begin
  ACanvas.Pen.Color := clGray;
  ACanvas.MoveTo(0, AHeight div 2);
  ACanvas.LineTo(AWidth, AHeight div 2);
  ACanvas.MoveTo(AWidth div 2, 0);
  ACanvas.LineTo(AWidth div 2, AHeight);
  ACanvas.Pen.Color := AColor;

  pos := -AWidth / Scale / 2;
  lastPoint := Point(-MaxInt, 0);

  repeat
    y := AFunction(pos);

    thisPoint := Point(AWidth div 2 + round(pos * Scale), AHeight div 2 - round(y * Scale));
    ACanvas.MoveTo(lastPoint.X, lastPoint.Y);
    ACanvas.LineTo(thisPoint.X, thisPoint.Y);
    lastPoint := thisPoint;

    pos := pos + (1 / Scale);
  until pos >= AWidth / Scale / 2;
end;


مثال:

  Plot(Image1.Canvas, Image1.Width, Image1.Height, Fx1, 20, clRed);
  Plot(Image1.Canvas, Image1.Width, Image1.Height, Fx2, 100, clBlue);

----------


## یوسف زالی

- رسم یک تابع روی محور های قطبی

خب مواقعی هم هست که تابع در مختصات دکارتی نیست.
در این گونه مواقع معمولا دستگاه قطبی کاربرد داره.
نمونه سورس زیر کمک می کنه که بتونید تابعتون رو در این دستگاه رسم کنید.
دقت کنید که به جای دادن x و گرفتن y در اینجا باید Theta بدید و Ro بگیرید. یعنی زاویه می دید و طول می گیرید.
نکته: زوایا باید در مقیاس رادیان باشند.

کد:

type
  TFuncForPlot = function(theta: real): real;

function Fx1(theta: real): real;
begin
  Result := theta;
end;

function Fx2(theta: real): real;
begin
  Result := sin(theta);
end;

function Fx3(theta: real): real;
begin
  Result := sin(theta) * cos(theta);
end;

procedure Plot(ACanvas: TCanvas; AWidth, AHeight: integer; AFunction: TFuncForPlot; Scale: integer; AColor: TColor);
var
  theta: real;
  ro: real;
  lastPoint, thisPoint: TPoint;
begin
  ACanvas.Pen.Color := clGray;
  ACanvas.MoveTo(0, AHeight div 2);
  ACanvas.LineTo(AWidth, AHeight div 2);
  ACanvas.MoveTo(AWidth div 2, 0);
  ACanvas.LineTo(AWidth div 2, AHeight);
  ACanvas.Pen.Color := AColor;

  theta := 0;
  lastPoint := Point(-MaxInt, 0);

  repeat
    ro := AFunction(theta);

    thisPoint := Point(AWidth div 2 + round(ro * cos(theta) * Scale), AHeight div 2 - round(ro * sin(theta) * Scale));

    ACanvas.MoveTo(lastPoint.X, lastPoint.Y);
    if lastPoint.X <> -MaxInt then
      ACanvas.LineTo(thisPoint.X, thisPoint.Y);
    lastPoint := thisPoint;

    theta := theta + PI / 360;
  until theta >= 2 * PI;
end;


مثال:

  Plot(Image1.Canvas, Image1.Width, Image1.Height, Fx1, 20, clRed);
  Plot(Image1.Canvas, Image1.Width, Image1.Height, Fx2, 100, clBlue);
  Plot(Image1.Canvas, Image1.Width, Image1.Height, Fx3, 400, clGreen);

----------


## یوسف زالی

- استفاده از ریشه یابی در حل معکوس توابعی که سورس تبدیل آن را در اختیار نداریم

فرض کنید تابعی دارید که یک تاریخ میلادی رو به شمسی تبدیل می کنه. حالا اگر بخواهید شمسی رو به میلادی تبدیل کنید چه راهی دارید؟
باید یک تابع دیگه بنویسید که این کار رو کنه؟ یعنی تمام تبدیلات معکوس رو هم انجام بدید. اگر سورس رو نداشتید تا بفهمید الگوریتم چیه که معکوسش کنید، یا با یک DLL طرف بودید، یا سورس خیلی پیچیده بود چی؟ اگر چند ضابطه ای بود که دیگه واویلا..

در اینجا با روشی که ارائه می شه می تونید بدون داشتن اون تابع این کار رو انجام بدید.
برای استفاده از این روش؛ تابع تبدیل باید اکیدا یکنوا باشه، یعنی یا فقط صعودی یا فقط نزولی. در این جا مثلا برای تبدیل شمسی این شرط برقراره. تابع تبدیل با زیاد شدن ورودی خروجی هم ریاد تر می شه و می شه مقایسه ای روی اونها انجام داد.

برای تبدیلات خطی روش Divide and Conquer دودویی و برای معادلات غیر خطی روش نیوتونی پیشنهاد می شه.

روش زیر برای تبدیل کمیت های سیستم های مختلف عالی هست.

مثال:
تابعی رو تصور کنید که عددی رو می گیره و در خروجی هم یک عدد برمی گردونه. ما این تابع رو نداریم که ببینیم چه می کنه اما می دونیم که اکیدا یکنوا هست.
مثلا تابع زیر:

function Fx(x: real): real;
begin
  if x < -5 then
    Result := x
  else if x < 0 then
    Result := -x * x / 5
  else if x < 5 then
    Result := x + sin(x * PI / 5)
  else if x < 8 then
    Result := x
  else
    Result := x * x / 8;
end;


برای تبدیل معکوس این تابع حتی اگر کدش رو داشته باشیم ساعتها زمان نیاز هست که معادلاتش رو حل کنیم، تست کنیم و ...

اما روش:

type
  TFunc = function(x: real): real;

function F_1x(AFunction: TFunc; FxValue, LeftBound, RightBound, Precision: real): real;
var
  Crawler, yCrawler: real;
begin
  if abs(AFunction(LeftBound) -FxValue) <= Precision then
    begin
    Result := LeftBound;
    Exit;
    end;

  if abs(AFunction(RightBound) -FxValue) <= Precision then
    begin
    Result := RightBound;
    Exit;
    end;

  Crawler := (RightBound + LeftBound) / 2;
  yCrawler := AFunction(Crawler);

  if abs(yCrawler -FxValue) <= Precision then
    Result := Crawler
  else if yCrawler < FxValue then
    Result := F_1x(AFunction, FxValue, Crawler, RightBound, Precision)
  else
    Result := F_1x(AFunction, FxValue, LeftBound, Crawler, Precision);
end;


مثال:

procedure TForm1.Button1Click(Sender: TObject);
var
  x, y: real;
begin
  y := 3.2233776277; // this is a test value for Fx(2.237)

  x := F_1x(Fx, y, -100, 100, 0.0000000001); // if you want exact value set this to zero

  ShowMessage(FloatToStr(x));
end;


ببینید، بدون داشتن اصل تابع، فهمیدیم که مقدار اصلی چی بوده. یعنی تابع رو برعکس کردیم.
همین کار رو به سادگی می شه برای تبدیل تاریخ انجام داد.
دادن باند های چپ و راست خیلی مهمه. برای تاریخ می شه این کار رو کرد:
می دونیم که همیشه یک تاریخ میلادی بین 622 سال تا 623 سال از شمسی جلوتره.
باند ها رو می گذاریم تاریخ شمسی مون + 621 تا تاریخ شمسی مون + 624.
به همین سادگی.

امیدوارم مفید بوده باشه.
دوستان نظر بگذارید. دارم دلسرد می شم.

----------


## Ananas

تابع فیبوناچی :

function Fibonachy(const X : Extended):Extended;
const
  Fi : Extended = 1.618033988749894848204586834365638117;// (1.0 + Sqrt(5)) / 2.0
var
  FX : Extended;
begin
  FX := Floor(x);
  Result := (Power(Fi, FX) - Power(-Fi, -FX)) / Sqrt(5.0);
end;

----------


## یوسف زالی

آفرین کلک قشنگی بود. از عدد فی استفاده شده.
در لیست اضافه شد.

----------


## Ananas

فاکتوریل اعشاری و تابع گاما :
تعربف فاکتوریل از زبان ویکیپدیا:
http://fa.wikipedia.org/wiki/%D9%81%...B1%DB%8C%D9%84

unit Factorial_Unit;

interface

uses
    math;

const
    FM_INFINITY = 1.0 / 0.0;
    FM_PI = 3.141592653589793238462643383279502884197169399375  1;
    FM_LN2PI_2 = 0.9189385332046727417803297364056176398613;
    FM_Bn : array [0..10] of extended = (1.0 ,
     1.6666666666666666666666666666666666666666E-1 ,
    -3.3333333333333333333333333333333333333333E-2 ,
     2.3809523809523809523809523809523809523809E-2 ,
    -3.3333333333333333333333333333333333333333E-2 ,
     7.5757575757575757575757575757575757575757E-2 ,
    -2.5311355311355311355311355311355311355311E-1 ,
     1.1666666666666666666666666666666666666666    ,
    -7.0921568627450980392156862745098039215686    ,
     5.4971177944862155388471177944862155388471E1  ,
    -5.2912424242424242424242424242424242424242E2);

function FactBn(const X : extended):extended;
function Factorial(const X : extended):extended;
function Gamma(const X : extended):extended;

implementation

function FactBn(const X : extended):extended;
var
    i: integer;
    x2  , xn :extended;
begin
    Result := FM_LN2PI_2 + (x + 0.5) * ln(abs(x)) - x;
    x2 := x * x;
    xn := 1 / x;
    for i := 1 to 8 do
    begin
        xn := xn * x2;
        Result := Result + (FM_Bn[i]) / (2.0 * i * (2.0 * i - 1.0) * xn);
    end;
    if Result <> FM_INFINITY then
        Result := exp(Result);
end;

function Factorial(const X : extended):extended;
var
  I: Integer;
  x_n : extended;
begin
    if (X > 1725.0) or ((X < 0.0) and (Trunc(X) = X)) then
    begin
        Result := FM_INFINITY;
        Exit;
    end;
    if (X = 0.0) then
    begin
      Result := 1.0;
      Exit;
    end;
    if ((X < 10.0) and (X > -10.0)) then
    begin
        if (X < 0.0) then
        begin
            x_n := X - 10.0;
            Result := FactBn(x_n);
            for i := 1 to 10 do
            begin
                x_n := x_n + 1.0;
                Result := Result * x_n;
            end;
        end
        else
        begin
            x_n := X + 10.0;
            Result := FactBn(x_n);
            for i := 1 to 10 do
            begin
                Result := Result / x_n;
                x_n := x_n - 1.0;
            end;
        end;
    end
    else
        Result := FactBn(X);
    if X < 0.0 then
        Result := Result / (Sin(-FM_PI * X)) / 2.0;
end;

function Gamma(const X : extended):extended;
begin
    Result := Factorial(X - 1)
end;

end.


تابع FactBn مقدار فاکتوریل رو بر اساس فرمول و با استفاده از اعداد برنولی بدست میاره ولی چون در بازه ی  -10 تا 10 از دقت اعشار کمتری برخورداره قسمتی از محاسبه رو در تابع Factorial با استفاده از روش معمولی فاکتوریل (ضرب های متوالی) انجام میدیم تا زودتر و دقیق تر به جواب برسیم. یعنی ترکیبی از روش معمولی (با استفاده از تعریف فاکتوریل)  و استفاده از فرمول استرلینگ. 
تابع گاما هم که دقیقا فرم فاکتوریل رو داره فقط با اختلاف ورودی 1.
فاکتوریل برای اعداد صحیح منفی تعریف نمی شه و مقدارش بینهایت هست ولی اعداد اعشاری منفی هم به دلیل اینکه (اگه اشتباه نکنم) تابع Ln ورودی منفی نمیگیره باید به شکل اعداد مختلط محاسبه شن پس بر ضریبی از سینوس تقسیم میشن.

----------


## developing

با سلام

به خاطر اینکه مایوس نشی و به کارت ادامه بدی این پست رو نوشتم والا

این توابع جز توابع خاص هستند و کلا ریاضیات جز علوم خاصی هستند که هر کسی به کارش نمیاد که بخواد بیاد اینجا از شما تشکر کنه یا نظر بذاره و یا سوالی بپرسه.

اما باید آفرین گفت به شما و امثال شما که تجربیات خودتون رو با دیگران به اشتراک میذارید

امیدوارم روزی برسه که این کارهای بزرگ تبدیل به کتابخانه ای بشود و به صورت یکجا و کنار دلفی نصب و استفاده گردد.

باز هم تشکر

----------


## یوسف زالی

بیشتر منظور من از این مبحث، کاربردی کردن ریاضیات در راه حل های بهینه برنامه نویسیه.
وگرنه فرمول که همون فرموله.
اگر خوب بلد باشیم از ریاضیات استفاده کنیم برنامه نویسی بهتری خواهیم داشت.
این که بدونیم و حضور ذهن داشته باشیم که می شه از چه روشهایی به جواب رسید دستمون رو باز می گذاره که به راه حل های بهتری برسیم.

----------


## یوسف زالی

- استفاده از قوانین منطقی در برنامه نویسی

1- قانون دمورگان: بیان می کند که اگر چند گزاره And (یا Or) شده باشند، نقض تمام گزاره ها مساوی است با نقض تک تک گزاره ها اما با Or (یا And) و برعکس.

(P & Q)' <=> P' | Q'
(P | Q)' <=> P' & Q'


کاربردش چیه؟
ما تو برنامه هامون خیلی مواقع نیاز داریم که Enabled بودن یک دکمه رو (مثلا در مواقعی مثل ذخیره) با Change شدن ادیت ها یا سایر متغیرها ریست کنیم و با توجه به شرایطی اون رو true یا false کنیم.
مثال:

  Button1.Enabled := (not SameText(Edit1.Text, '')) and (not SameText(Edit2.Text)) and (not SameText(Edit3.Text));

از نظر جبری این می شه این:

  T := (not P) and (not Q) and (not R);

با استفاده از دمورگان:

  T := not (P or Q or R);

یا همون:

  Button1.Enabled := not (SameText(Edit1.Text, '') or SameText(Edit2.Text, '') or SameText(Edit3.Text, ''));

این رو همه می دونیم، مهم اینه که به جا ازش استفاده کنیم تا کد کوتاه تر و خواناتری بدست بیاریم.

2- جدول کارنو: اگر دروس مدارهای منطقی و دیجیتال رو یادمون باشه می دونیم چیه، اگر نه اینجا

دیگه مثال براتون نمی گذارم، دقیقا مثل مثال اول شرایطی هست که تعداد متغیرها برای ارزش دهی یک متغیر دیگه اونقدر زیاده که عملا کار از دست آدم در می ره.
در این مواقع با ساده کردن با استفاده از جدول کارنو متوجه می شید که چقدر کارها ساده می شه و چقدر درک جدیدی از این مسائل بدست میارید.

- استفاده از شمای گرافیکی
همون طور که در اینجا برای دمورگان مثال زده شده.
اوضاع هر چقدر که پیچیده شده باشه باز هم این قوانین کمک شایانی در کدنویسی سره می کنند.

کدهایتان کوتاه باد!

----------


## یوسف زالی

- کاربرد عملگر های بیتی در برنامه نویسی
فرض کنید لیستی از Boolean ها دارید که می خواهید به فرم دوم پاسش بدید. چطور این کار رو می کنید؟
یکی از راه هاش اینه که یک چک لیست درست کنید و اون رو بفرستید.
خب اگر مجبور شدید اونها رو ذخیره کنید یا به دیتابیس بفرستید چه می کنید؟
برای هر متغیر بولی یک فیلد؟
اگر تعداد 40 تا بود چی؟
یک مثالش می تونه این باشه که می خواهید کاربر بنا به میل خودش بتونه فیلدهای گرید برنامه شما رو ببینه یا نبینه، شما برای هر گرید به تعداد ستونهاش باید یک بول بگیرید و اون رو ذخیره کنید؟

در روشی که گفته می شه کافیه یک فیلد از نوع عددی در نظر گرفته بشه.

procedure CheckBoxToInt(CheckListBox: TCheckListBox; var Value: int64);
var
  i: integer;
begin
  Value := 0;
  for i := 0 to CheckListBox.Count -1 do
    Value := Value shl 1 + Ord(CheckListBox.Checked[i]);
end;

procedure IntToCheckBox(const Value: int64; CheckListBox: TCheckListBox);
var
  i: integer;
  f: int64;
begin
  f := 1;
  for i := CheckListBox.Count -1 downto 0 do
    begin
    CheckListBox.Checked[i] := (Value and f) > 0;
    f := f shl 1;
    end;
end;


دلم نیومد این مطلب رو اضافه نکنم، بی ربط به مساله هست، اما برای این مطلب کوچیک دیگه تاپیک نزنم:

می تونید به جای درست کردن Stack و Queue هم از رشته استفاده کنید!

البته به شرطی که داده هاتون در یک بایت جا بشه. برای داده های بزرگتر می تونید از نوع Wide استفاده کنید و هر کاراکتر رو معادل آدرسی از حافظه که داده شما در اونجا قرار داره بگیرید!! این طوری حتی می شه رشته ها و اشیا رو Push و Pop کرد!  :چشمک: 
کد:

// for Stack
procedure Push(var S: string; Value: char);
begin
  S := Value + S;
end;

function Pop(var S: string): char;
begin
  Result := S[1];
  Delete(S, 1, 1);
end;

function GetStackLength(S: string): integer;
begin
  Result := length(S);
end;

// for Queue
procedure Enqueue(var S: string; Value: char);
begin
  S := S + Value;
end;

function Dequeue(var S: string): char;
begin
  Result := S[1];
  Delete(S, 1, 1);
end;

function GetQueueLength(S: string): integer;
begin
  Result := length(S);
end;


مثال:

procedure TForm1.Button1Click(Sender: TObject);
var
  Stack, Queue: string;
  Data: char;
begin
  Stack := '';
  Queue := '';

  Push(Stack, 'A');
  Push(Stack, 'B');
  Push(Stack, 'C');

  Data := Pop(Stack);
  ShowMessage(Data);
  Data := Pop(Stack);
  ShowMessage(Data);
  Data := Pop(Stack);
  ShowMessage(Data);

  Enqueue(Queue, 'A');
  Enqueue(Queue, 'B');
  Enqueue(Queue, 'C');

  Data := Dequeue(Queue);
  ShowMessage(Data);
  Data := Dequeue(Queue);
  ShowMessage(Data);
  Data := Dequeue(Queue);
  ShowMessage(Data);
end;


امیدوارم لذت برده باشید.

----------


## Ananas

بسط تیلور توابع سینوس و کسینوس :
ویکیپدیا : http://en.wikipedia.org/wiki/Taylor_series


function SinTaylor(X:Extended; Count : Integer = 10):Extended;
const
  PI_314 = 3.14159265358979323846;
var
  xx, x_n, inv_fact, x_floor : Extended;
  i, j, x_div_pi: Integer;
begin
  x_div_pi := Trunc(X / PI_314);
  x_floor := X - PI_314 * x_div_pi;
  if (x_div_pi mod 2 <> 0) then
     x_floor := -x_floor;
  // ---------------------------------
  // x_floor : X;
  // ---------------------------------
  xx := x_floor * x_floor;
  x_n := x_floor;
  Result := x_n;
  inv_fact := 1; // 1!
  i := 1;
  while (i < Count * 2) do
  begin
    x_n := x_n * xx;
    i := i + 2;
    j := (i * i - i); // i * (i - 1)
    inv_fact := -inv_fact / j; // (2k - 1)!
    Result := Result + x_n * inv_fact;
  end;
end;


function CosTaylor(X:Extended; Count : Integer = 10):Extended;
const
  PI_314 = 3.14159265358979323846;
var
  xx, x_n, inv_fact, x_floor : Extended;
  i, j, x_div_pi: Integer;
begin
  x_div_pi := Trunc(X / PI_314);
  if (x_div_pi mod 2 <> 0) then
  begin
    if (X < 0) then
      x_floor := -X + PI_314 * (x_div_pi - 1)
    else
      x_floor := -X + PI_314 * (x_div_pi + 1);
  end
  else
    x_floor := X - PI_314 * x_div_pi;
  // ---------------------------------
  // x_floor : X;
  // ---------------------------------
  xx := x_floor * x_floor;
  x_n := 1.0;
  Result := x_n;
  inv_fact := 1; // 1!
  i := 0;
  while (i < Count * 2) do
  begin
    x_n := x_n * xx;
    i := i + 2;
    j := (i * i - i); // i * (i - 1)
    inv_fact := -inv_fact / j; // (2k)!
    Result := Result + x_n * inv_fact;
  end;
end;

مقادیر 3! و 5! و 7! و ... برای سینوس و همچنین مقادیر 2! و 4! و 6! و ... مرحله به مرحله بدست میاد. یعنی برای مثلا 7! باید 7 * 6 * 5! حساب بشه که 5! رو از مرحله ی قبل داریم. همچنین برای توان ها هم همچین چیزی استفاده شده یعنی متغیر xx با مقدار x * x تو هر مرحله ضرب میشه در مقدار x_n.
قبل از انجام محاسبه هم مقدار هم ارز زاویه رو بین 0 تا 2 پی محاسبه میکنیم و در متغیر x_floor میریزیم تا از اون بجای X استفاده کنیم. برای دقت اعشار بیشتر چون این بسط ها حول 0 سریعتر به جواب میرسند.
--------------------------------------------------------
کدهای ویرایش شده و بهتر و سریعتر (با جملات کمتر به جواب میرسن)

function Cos_Taylor(X:Extended; Count : Integer = 7):Extended;
const
  PI_MUL_2   = 6.283185307179586476925286766559; // 2 * 3.14159265358979323846;
  PI_314     = 3.1415926535897932384626433832795;
  PI_DIVBY_2 = 1.5707963267948966192313216916398;
var
  xx, x_n, x_local : Extended;
  i: Integer;
begin
  x_local := Abs(X);
  x_local := x_local - PI_MUL_2 * Trunc(x_local / PI_MUL_2);
  if (x_local > PI_314) then
    x_local := PI_MUL_2 - x_local;

  if (x_local > PI_DIVBY_2) then
  begin
    x_local := x_local - PI_314;
    x_n := -1.0;
  end else
    x_n := 1.0;
  // ---------------------------------
  // x_local : X;
  // ---------------------------------
  xx := x_local * x_local;
  Result := x_n;
  i := 0;
  while (i < Count * 2) do
  begin
    Inc(i, 2);
    x_n := -x_n * (xx / (i * i - i)); // i * (i - 1)
    Result := Result + x_n;
  end;
end;


function Sin_Taylor(X:Extended; Count : Integer = 7):Extended;
const
  PI_MUL_2   = 6.283185307179586476925286766559; // 2 * 3.14159265358979323846;
  PI_314     = 3.1415926535897932384626433832795;
  PI_DIVBY_2 = 1.5707963267948966192313216916398;
var
  xx, x_n, x_local : Extended;
  i: Integer;
begin
  x_local := Abs(X);
  x_local := x_local - PI_MUL_2 * Trunc(x_local / PI_MUL_2);
  if (x_local > PI_314) then
    x_local := -(PI_MUL_2 - x_local);

  if (x_local >  PI_DIVBY_2) then x_local :=   PI_314 - x_local;
  if (x_local < -PI_DIVBY_2) then x_local := -(PI_314 + x_local);
  // ---------------------------------
  // x_local : X;
  // ---------------------------------
  xx := x_local * x_local;
  if (X < 0) then x_n := -x_local
  else            x_n :=  x_local;
  Result := x_n;
  i := 1;
  while (i < Count * 2) do
  begin
    i := i + 2;
    x_n := -x_n * (xx / (i * i - i)); // i * (i - 1)
    Result := Result + x_n;
  end;
end;

محاسبات بین 0 تا p/2 هستن. بخاطر اینکه این بسط ها حول 0 سریع به جواب میرسن بقیه ی مقادیر انتقال داده میشن تو این بازه.

----------


## یوسف زالی

- محاسبات پرتابه در بازی Angry Birds
در این پست محاسبات ساده ی بازی مشهور پرندگان خشمگین رو براتون می گذارم اما قبلش طبق معمول یکم توفیق اجباری!

طبق قوانین فیزیک حرکت یک پرتابه از دو مؤلفه ی حرکت با سرعت ثابت افقی و حرکت با شتاب منفی در راستای عمود تبعیت می کنه.
یعنی در راستای افقی اگر از بالا به پرتایه نگاه کنیم حس می کنیم با سرعت ثابتی داره تو یک خط حرکت می کنه.
اگر از روبرو ببینیم فکر می کنیم که می ره بالا و میفته پایین مثل سقوط آزاد.
حرکت کلی از مجموع این دو حرکت بدست میاد.

X = V.(x) t
Y = -1/2 g t^2 + V. (y) t


برای تفکیک راستاها از توابع مثلثاتی استفاده می شه.

یک نکته دیگه اینکه تابع Hypot وتر یک مثلث قائم رو به ما می ده.

اگر خواستید اصطکاک هوا و وزش باد رو هم به سادگی می شه بهش اضافه کرد.

----------


## Ananas

بسط تیلور توابع Ln و exp :

function Ln_Taylor(X : Extended; Count : Integer = 5):Extended;
const
  n_0_1 = 0.01;
  ln_1_01 = 0.00995033085316808284821535754426; // Ln(1.01)

  n_1_0_High = (1.0 + n_0_1);
  n_1_0_Low  = (1.0 - n_0_1);

  arr : array[0..21] of Extended = (
    1.01,                                      // 1.01 ^ (2 ^ 0  =       1)
    1.0201,                                    // 1.01 ^ (2 ^ 1  =       2)
    1.04060401,                                // 1.01 ^ (2 ^ 2  =       4)
    1.0828567056280801,                        // 1.01 ^ (2 ^ 3  =       8)
    1.172578644923698520518625612016,          // 1.01 ^ (2 ^ 4  =      16)
    1.3749406785310970541622913505711,         // 1.01 ^ (2 ^ 5  =      32)
    1.8904618694795535717494712641154,         // 1.01 ^ (2 ^ 6  =      64)
    3.5738460799561286443002337196205,         // 1.01 ^ (2 ^ 7  =     128)
    12.77237580321778745481813393975,          // 1.01 ^ (2 ^ 8  =     512)
    163.13358365862322124527961077082,         // 1.01 ^ (2 ^ 9  =    1024)
    26612.566117305021291272917047288,         // 1.01 ^ (2 ^ 10 =    2048)
    708228675.34793125625127947801295,         // 1.01 ^ (2 ^ 11 =    4096)
    501587856585085410.3329144226122,          // 1.01 ^ (2 ^ 12 =    8192)
    2.515903778736202094338585203235e+35,      // 1.01 ^ (2 ^ 13 =   16384)
    6.3297718238591005455779113716959e+70,     // 1.01 ^ (2 ^ 14 =   32768)
    4.0066011342120564182784035050917e+141,    // 1.01 ^ (2 ^ 15 =   65536)
    1.6052852648669336927937427546953e+283,    // 1.01 ^ (2 ^ 16 =  131072)
    2.5769407815989014605653704987392e+566,    // 1.01 ^ (2 ^ 17 =  262144)
    6.6406237918675571564214160686182e+1132,   // 1.01 ^ (2 ^ 18 =  524288)
    4.4097884345117453067721383354009e+2265,   // 1.01 ^ (2 ^ 19 = 1048576)
    1.9446234037153549426176998268801e+4531,   // 1.01 ^ (2 ^ 20 = 2097152)
    3.7815601822774923352439350246879e+9062    // 1.01 ^ (2 ^ 21 = 4194304)
    );
var
  x_local, b, bb, b_n : Extended;
  scale_count : Int32;
  i, j : Integer;
begin
    x_local := X;
    scale_count := 0;
  //------------------------------------------
  //    Ln(A/B) = Ln(A)   - Ln(B)
  //    Ln(A)   = Ln(A/B) + Ln(B)
  //    .......... (if B == 1.01 then)
  //
  //    Ln(A)   = Ln(A / 1.01) + Ln(1.01)
  //
  //    ..........
  //    for i = 1 to count do
  //    {
  //        x /= 1.01
  //    }
  //    return Ln(x) + count * Ln(1.01)
  //
  //------------------------------------------
    j := 22;
    while ((x_local > n_1_0_High) and (j > 0)) do // x > 1.0
    begin
        Dec(j);
        while ((x_local > n_1_0_High) and (arr[j] < x_local)) do
    begin
            x_local := x_local / arr[j];
            scale_count := scale_count + Integer(1 shl j);
        end;
    end;
    j := 22;
    while ((x_local < n_1_0_Low) and (j > 0)) do // x < 1.0
    begin
        Dec(j);
        while ((x_local < n_1_0_Low) and ((1.0 / arr[j]) > x_local)) do
        begin
            x_local := x_local * arr[j];
            scale_count := scale_count - Integer(1 shl j);
        end;
    end;
// ---------- Start Taylor block -----------------------------------------------
//
//                  b^3    b^5     b^6   b^7
//  Ln(a) = 2 ( b + ---- + ---- + ---- + ---- + ... )
//                   3      5      7      9
//  where :
//        a - 1
//  b = --------
//        a + 1
//------------------------------------------------------------------------------
    b := (x_local - 1.0) / (x_local + 1.0);
    Result := b;

    b_n := b;
    bb := b * b;
  i := 3;
  while (i < (Count * 2)) do
  begin
    i := i + 2;
        b_n := b_n * bb;
        Result := Result + b_n / i;
    end;

    Result := Result * 2.0;
// ---------- End Taylor block -------------------------------------------------
    Result := result + (ln_1_01 * scale_count);
end;

این بسط حول نقطه ی 1 سریع به جواب میرسه بخاطر همین تا جایی که میشه اعدادمون رو با روابط ریاضی میبریم نزدیک این مقدار بعد که Ln رو محاسبه کردیم دوباره با استفاده از روابط مذکور برمیگردونیم به مقدار مورد نظرمون. 
به همین خاطر اول یک عدد نسبتا نزدیک به یک مثلا 1.01 رو انتخاب کردم بعد یک آرایه از توان های اون ساختم به این شکل :
اولین عضو آرایه = 1.01
دومین عضو آرایه = عضو قبلی ضرب در خودش
سومین عضو آرایه = عضو قبلی ضرب در خودش
...
بعد عدد X رو تا جایی که میشه به این اعداد تقسیم کردم تا کلا به ضریبی از 1.01 تقسیم کرده باشیم (البته به مقدار کافی، نه زیاد نه کم). بعد Ln رو حساب میکنیم بعد از محاسبه به تعدادی که تقسیم بر 1.01 کردیم به همون تعداد با Ln(1.01) جمع میکنیم. جواب بدست میاد. 
برای اعداد کوچک تر از 1 هم تقسیم به ضرب تبدیل میشه و جمع به منها.
---------------------------------------------------
exp :

function Exp_Taylor(X:Extended; Count : Integer = 10):Extended;
const
  arr_pow2 : array[0..12] of Extended = (
    2.00000000000000000000,
    4.00000000000000000000,
    16.0000000000000000000,
    256.000000000000000000,
    65536.0000000000000000,
    4294967296.00000000000,
    18446744073709551616.0,
    3.4028236692093846346337460743177e+38,
    1.1579208923731619542357098500869e+77,
    1.3407807929942597099574024998206e+154,
    1.797693134862315907729305190789e+308,
    3.231700607131100730071487668867e+616,
    1.0443888814131525066917527107166e+1233
  );
  e = 2.7182818284590452353602874713527;
var
  i, j: Integer;
  x_n, x_local, e_pow : Extended;
  pow2_count : Int32;
begin
  x_local := Abs(X);
  pow2_count := 0;
  j := 13;
  while ((x_local > 0.01) and (j > 0)) do
  begin
    Dec(j);
    while ((x_local > 0.01) and (0.001 < (x_local / arr_pow2[j]))) do
    begin
      x_local := x_local / arr_pow2[j];
      pow2_count := pow2_count + Integer(1 shl (j));
    end;
  end;

  (*
  x_local := Abs(X);
  j := Trunc(x_local);
  x_local := x_local - j;
  *)
//---------------------------
  Result := 1.0;
  x_n := 1.0;
  for i := 1 to Count do
  begin
    x_n := x_n * (x_local / i);
    Result := Result + x_n;
  end;
//---------------------------
  (*
  e_pow := 1.0;
  for i := 1 to j do
    e_pow := e_pow * e;
  Result := Result * e_pow;
  if X < 0 then
    Result := 1.0 / Result;
  *)

  for i := 1 to pow2_count do
    Result := Result * Result;
  if X < 0 then
    Result := 1.0 / Result;
end;

برای exp هم روشی مشابه داریم. به تعداد کافی عدد رو بر 2 تقسیم میکنیم تا کوچک بشه برسه نزدیک صفر. بعد exp رو حساب میکنیم بعد از محاسبه عدمون رو به همون تعداد که X رو بر 2 تقسیم کردیم، به توان 2 میرسونیم. یعنی اگر n بار به دو تقسیم کردیم، عدد به دست اومده از exp رو به توان 2 به توان n میرسونیم. یعنی یک حلقه ی n مرحله ای ه هر بار عدد رو در خودش ضرب میکنیم.
روش دیگه ای که اون رو تو متن کد غیر فعال کردم هم روشی مشابه هست که میایم بخش اعشاری عدد رو نگه میداریم و بخش صحیح رو ازش یگیریم و exp رو برای بخش اعشاری بین 0 تا 1 حساب میکنیم بعد به تعدادی که در بخش صحیح ، 1 داریم (خود بخش صحیح) عددمون رو در e که exp عدد 1 هست ضرب میکنیم . این روش هم خوبه ولی روش تقسیمات متوالی اجازه میده که عددمون بیشتر به صفر نزدیک بشه. مثلا عدد 0.9 رو که نزدیک صفر هست میتونیم با تقسیمات متوالی بر 2 باز هم ک.چیکتر کنیم. پس من روش تقسیم به 2 رو ترجیح میدم.

----------


## Ananas

تابعی برای به توان رساندن همه ی اعداد از جمله اعداد منفی .(اعداد منفی را نمی توان به توان عدد حقیقی رساند چون لازمه که در فرمول زیر :

a ^ b = e^(ln(a) * b)

باید از عدد پایه لگاریتم گرفته بشه که تابع Ln برای اعداد منفی تعریف نشده پس برای به توان رساندن پایه نباید منفی باشه. اما از طرفی میدونیم که وقتی یک عدد منفی به توان یک عدد زوج میرسه علامتش مثبت میشه و وقتی به توان عددی فرد میرسه علامتش منفی میشه (توان صحیح نه اعشاری). برای اینکه از ضریب -1 به شکل دستی استفاده نکنیم و تابع در تمام نقاط جواب بده از جمله توان های اعشاری من عدد پایه رو قدر مطلق گرفتم و در عوض نتیجه ی تابع رو در یک کسینوس ضرب کردم. نتیجه :

function PowerAll(const Base: Extended; const Exponent: Extended):Extended;
begin
    if (Base = 0) then
        Result := 0.0
    else if (Base > 0) then
        Result := Exp(Ln(Base) * Exponent)
    else
        Result := Exp(Ln(-Base) * Exponent) * Cos(Exponent * PI);
end;

نمیدونم از نظر ریاضی روش درستی هست یا نه ولی ظاهرا جواب میده. نظر بدید لطفا.
نمونه ای از کار این تابع با رسم منحنی با C++‎‎Builder :
Math_Power_for_All_Numbers.zip
با حرکت افقی موس تابعی رسم میشه که عددی متناسب با مکان افقی موس رو به توان x میرسونه و با حرکت عمودی موس تابعی رسم میشه که x رو به توان عددی متناسب با مکان عمودی موس میرسونه.

----------


## یوسف زالی

فکر می کنم در قسمت کسینوس یک اشکال وجود داشته باشه.
ظاهرا این فرمول در خصوص توان های صحیح درسته و روی توانهای اعشاری کسینوس اعشاری می شه.
درست می گم؟ البته هنوز سراغ اثباتش نرفتم. همین طوری چشمی گفتم.

----------


## Ananas

> فکر می کنم در قسمت کسینوس یک اشکال وجود داشته باشه.
> ظاهرا این فرمول در خصوص توان های صحیح درسته و روی توانهای اعشاری کسینوس اعشاری می شه.
> درست می گم؟ البته هنوز سراغ اثباتش نرفتم. همین طوری چشمی گفتم.


 آفرین نکتش تو همینه و نمیشه گفت اشکال (شاید تو کتابای ریاضی همچین تعریفی وجود نداشته باشه و تقریبا من در آوردی هست) چون مقصودم همین بود که برای همه ی اعداد از جمله اعشاری درست کار کنه و تابع در همه جا پیوسته باشه غیر از صفر که تو اعداد بین -1 و 1 میشه 1 / x به توان n که در صفر جواب نداره. وگرنه دلیلی نداشت از کسینوس استفاده کنم میومدم از یک If استفاده میکردم و در -1 و 1 ضرب میکردم. مثلا اگر عددی بخواد به توان -1.05 برسه تکلیفش چیه؟ این جزو تتوان های زوج هست یا فرد؟ هیچ کدوم ولی میدونیم که نزدیک فرد هست.
اگه فایل اجرایی رو که با C++‎Builder ساختم ببینی احتمالا نیاز به تست هم نداشته باشه چون نمودار رو رسم میکنه و با حرکت موس شما میتونی ببینی که وقتی (تو قسمت منفی) عدد توان میره نزدیکه یک عدد صحیح زوج مثل -2 ، منحنی به سمت بالا حرکت میکنه تا تابع نسبت به محور y قرینه بشه و وقتی عدد توان میره نزدیک یک عدد فرد صحیح مثل -3 می بینیم که نمودار در قسمت منفی محور x میاد به سمت پایین تا نسبت به مرکز مختصات قرینه بشه. نمودار سبز رنگ رو دقت کنید.
 یعنی با تغییر توان، قسمت منفی نمودار دائم بالا پایین میشه (اعداد زوج و فرد توسط کسینوس در -1 و 1 ضرب میشن و اعداد اعشاری اطراف اونها هم در اعداد بین -1 و 1 ضرب میشن).

----------


## یوسف زالی

- به دست آوردن تعداد ارقام در مبنای خاص

عددی داریم مثلا در مبنای دو، می خواهیم ببینیم چند تا رقم داره، همین کار برای مبنای 5 چی می شه؟
در این روش نیازی به تبدیل به رشته ندارید.


function GetDigitCount(N: Cardinal; Base: word = 10): word;
begin
  Result := Floor( ln(N) / ln(Base) ) +1
end;



مثال:


  Label1.Caption := IntToStr(GetDigitCount(100));
  Label1.Caption := IntToStr(GetDigitCount(123456));
  Label1.Caption := IntToStr(GetDigitCount(256, 2));
  Label1.Caption := IntToStr(GetDigitCount(700, 7));

----------


## یوسف زالی

- ساخت n عدد تصادفی بدون تکرار
برای این کار کافیه از روشی موسوم به Shuffling استفاده کنید. به این صورت که اعداد رو پشت سر هم می نویسید، بعد اونها رو بر می زنید!

procedure TForm1.Button1Click(Sender: TObject);
var
  i, n: integer;
  lst: TStringList;
begin
  n := 1000;
  Randomize;

  lst := TStringList.Create;

  for i := 1 to n do
    lst.Add(IntToStr(i));

  for i := 1 to n do
    lst.Exchange(random(n), random(n));

  Memo1.Lines.Text := lst.Text;

  lst.Free;
end;

----------


## یوسف زالی

*- محاسبه میانگین لحظه ای و اعداد بزرگ (میانگین به فرم بازگشتی)*
طرح مشکل:
فرض کنید اعدادی دارید که خودشون در متغیرهاشون جا می شن اما مجموعشون نمی شه. یعنی چی؟
مثلا بزرگترین ظرفی که دارید 8 بایتی هست، این یعنی اعداد باید در رنج 0 تا 2 به توان 8 منهای یک باشه (برای اعداد حسابی)
خب، حالا به ما 2000 تا از این عدد ها می دن می گن میانگینش چقدره؟
چه می کنید؟
روش عادی این طوره که اول باید همه رو جمع کنیم، بعد تقسیم کنیم به تعدادشون، اما این کار یعنی به احتمال زیاد مجموع اعداد دیگه در ظرف 8 بایتی متغیر ما جا نخواهد شد.
(نگید مجموع رو در 16 بایت می ریزیم ها! منظورم بزرگترین ظرف در دسترس کامپایلرتونه)
خب، چه کنیم؟
شاید بگید از یونیت اعداد بزرگ استفاده می کنم. اما این کار بسیار برنامه رو کند می کنه.

از طرفی اگر مثلا روی نموداری دارید میانگین لحظه ای می گیرید و نمایشش می دید، برای هر بار گرفتن میانگین نیاز دارید تمام اعدادی رو که تا به حال وارد شدند جایی نگه داری کنید یا این که از نو مجموعشون رو داشته باشید که داستان OverFlow پیش میاد. به این ترتیب باید بار حافظه رو به دوش بکشید یا این که تن به انجام دوباره محاسبات بدید.

روشی امروز تو تاکسی! به ذهنم رسید که وقتی رسیدم، با محاسبات ساده اثباتش مشخص شد.
اما روش کار:
ما باید بصورت بازگشتی کار کنیم، یعنی این که میانگین کل براساس میانگین های قبلی قابل محاسبه باشه، به زبان ساده تر یعنی این که در هر مرحله ای میانگین رو تا اون مرحله درجا محاسبه کنیم و منتظر عدد بعدی نباشیم.
فرض کنیم A, B, C سه عدد باشند.
این درست نیست که بگیم میانگین کل مساوی است با میانگین A و B، بعد میانگین C با جواب میانگین قبلی.
این مساله با سه تا عدد جدا به سادگی قابل اثباته.
روش درستش اینه:

Avg(n) = (n-1)/n * Avg(n-1) + 1/n * X(n)

نمونه برنامش رو هم براتون می گذارم؛

// Old Fashion in Math unit
{
function Mean(const Data: array of Double): Extended;
begin
  Result := SUM(Data) / (High(Data) - Low(Data) + 1);
end;
}


function Mean(const Data: array of Double): Extended;
var
  i: integer;
begin
  Result := 0;
  for i := 1 to Length(Data) do
    Result := (i -1) / i * Result + 1/i * Data[i -1];
end;


procedure TForm1.Button1Click(Sender: TObject);
var
  Test: array[1..3] of Double;
begin
  Test[1] := 10;
  Test[2] := 15;
  Test[3] := 20;


  Label1.Caption := FloatToStr(Mean(Test))
end;


با استفاده از این کد می تونید مطمئن باشید که اگر داده هاتون سرریز نمی شوند، محاسبات میانگین هم سرریز نمی شود.
شاد باشید.

----------


## rahnema1

> function Mean(const Data: array of Double): Extended;
> var
>   i: integer;
> begin
>   Result := 0;
>   for i := 1 to Length(Data) do
>     Result := (i -1) / i * Result + 1/i * Data[i -1];
> end;


سلام
شما می خواهید میانگین یک آرایه ثابت را به دست بیارید بدون اینکه سر ریز بشه
فکر کنم این راه سریعتره:

function Mean(const Data: array of Double): Extended;
var
  i,n: integer;
begin
  Result := 0;
  n := Length(Data);
  for i := 0 to n-1 do
    Result := Result+Data[i]/n;
end;

----------


## یوسف زالی

> آرایه ثابت


حرفی از ثابت بودن زده نشده دوست عزیز.
روش شما درسته، اما باید از قبل تعداد اعداد رو بدونید. در روشی که معرفی شد، در هر لحظه میانگین تا اون لحظه محاسبه می شه.
کاربردش مثلا در نمایش چارت می تونه باشه.
شما در هر لحظه عددی رو که میاد رو می گیرید و با احتساب این عدد میانگین جدید رو محاسبه می کنید.
فرض کنیم N عدد داریم، میانگین به هر دو روش محاسبه می شه و روی نمودار می ره. حالا عدد N +1 ام میاد، روش شما الزام می کنه که از نو تمام محاسبات انجام بشه.
مرتبه ی زمانی الگوریتم شما N^2 هست و الگوریتم معرفی شده N.
حتما در جریان هستید که تفاوت چقدر وحشتناک می تونه باشه.
اگر اعداد از قبل معین هستند، روش شما روش بهتریه. اما تذکر شما در خصوص گنگ بودن مطلب به جاست و مطلب اصلاح می شود.

----------


## HosseinSaberi

> بیشتر منظور من از این مبحث، کاربردی کردن ریاضیات در راه حل های بهینه برنامه نویسیه.
> وگرنه فرمول که همون فرموله.
> اگر خوب بلد باشیم از ریاضیات استفاده کنیم برنامه نویسی بهتری خواهیم داشت.
> این که بدونیم و حضور ذهن داشته باشیم که می شه از چه روشهایی به جواب رسید دستمون رو باز می گذاره که به راه حل های بهتری برسیم.



سلام خدمت دوستان
البته این مباحث شاید کاربرد عملی به صورت مجزا نداشته باشه ولی در صورتیکه بلد باشید هم ذهن رو باز میکنه و هم میشه از مفاهیمش استفاده کرد.
مثلاً اینجا رو نگاه کنید. من پدرم برای حل این مسئله در اومد چون روش تبدیل And به Or رو بلد نبودم و فکر کنم تبدیلش یه سه روزی طول کشید ولی آقای You-see با استفاده از همین ریاضیات کاربردی توی تقریباً دو ساعت (به گفته خودش) حلش کرد.

----------


## یوسف زالی

*- شبیه سازی بیلیارد (برخورد بین چندین توپ)*

در این پست برخورد بین گوی ها رو براتون می گذارم.
در این مثال تعداد گوی ها کاملا دست خودتونه. در حقیقت این پروژه بازنویسی پروژه دوران دانشجویی بود برای بازی گردو بازی تحت پاسکال!!
نکات :
در برخورد، مجموع اندازه حرکت سیستم ثابت باقی می ماند.
در برخورد، مجموع انرژی سیستم ثابت می ماند.
در برخورد، سرعت مرکز جرم سیستم ثابت می ماند.

محاسبات یکم در دنیای ریاضیات دور می زنه، پس خیلی واردش نمی شم. می تونید از اینجا نمونه محاسبات رو ببینید.

برنامه کاستی هایی داره که دلیلش محاسبه لحظه برخورده. پس اگر قرار بود باهاش واقعا کاری انجام بدید، بهتره که یکم روی زمان برخورد کار کنید.

----------


## یوسف زالی

*- تابعی برای ارزیابی عبارات ریاضی*

این یونیت رو براتون آماده کردم، به راحتی می تونید عبارات ریاضی خودتون رو بهش بسپرید و خروجی مناسب رو ازش بگیرید.

مهم ترین نکته اینه که اولویت بندی و پرانتز گذاری ها رو خودش تشخیص می ده. مثلا 2 * 3 + 1 رو 7 می ده نه 8.

توابع و کلماتی که ساپورت می شن:

()
+
-
/
*
%
^
&
|
~
>>
<<
E
PI
abs
acos
asin
atan
ceil
cos
exp
floor
log
max
min
pow
random
round
sin
sqrt
tan

امیدوارم لذت ببرید.

مثال:

ShowMessage(EvalExpression('sin(0.2) - (cos(log(2)))*3'))

----------


## یوسف زالی

مشکل سر این ویرایشگر احمق سایت بود که خودش وسط کار تگ می ندازه.
یونیت رو در Delphi7 و Delphi XE6 تست گرفتم مشکلی نیست.
می تونید استفاده کنید.

----------


## Ananas

تابع Lerp برای درون یابی خطی بین دو مقدار به کار میره:

function Lerp(const p1, p2, s: Extended):Extended;
begin
    Result := p1 + s * (p2 - p1);
end;

این تابع دو مقدار دریافت میکنه به همراه پارامتر s که معمولا عددی بین 0.0 تا 1.0 هست تعیین میکنه که به کدوم عدد متمایل شه.
تابع Lerp برای بردار ها هم بکار میره به این شکل که روی هر مولفه ی متناظر از دو بردار اعمال میشه.
متلا:


function LerpVec3(V1, V2 : TVec3D; S: Single):TVec3D;
begin
    Result.x := V1.x + S * (V2.x - V1.x);
    Result.y := V1.y + S * (V2.y - V1.y);
    Result.z := V1.z + S * (V2.z - V1.z);
end;

----------


## Suniiman

سلام آقا. من به این یونیت شدیدا احتیاج دارم. ولی لینک دانلودشو ندیدم. تو سایت برنامه نویس، من یه کاربر مبتدیم و شاید بود و ندیدم. لطف کنید برام بفرستین. مطالب شما در مورد ریاضیات بسیار عالی بود. کلی استفاده کردم موفق باشید.

----------


## یوسف زالی

لینک دانلودش در همون پست وجود داره، چی رو ندیدید؟

----------


## Suniiman

> لینک دانلودش در همون پست وجود داره، چی رو ندیدید؟


برای من لینک فایلهای ضمیمه غیرفعاله

----------


## Suniiman

[QUOTE=یوسف زالی;2134555]*- تابعی برای ارزیابی عبارات ریاضی*


این یونیت رو براتون آماده کردم، 
من این یونیتو میخوااااام

----------


## یوسف زالی

https://barnamenevis.org/attachment.p...2&d=1416382724
بدون مشکل دانلود می شود

----------


## Suniiman

ممنون. با این لینک دانلود شد. الان دارم میروم دلواپسی ها را ز سر بیرون کنم، گر فلک با من نسازد چرخ را وارون کنم. خیلی لطف کردین.

----------


## Suniiman

سلام. یونیت ارزیابیه عبارت ریاضی رو اجرا کردم. دوتا دستور رو نمیشناسه:
PosEx
و
ValueFromIndex
احتمالا مال دلفیمه. من ورژن 6 رو دارم. میشه لطف کنید بفرمایید این دوتا تابع چیکار میکنن که خودم برنامه جایگزین عملکردشو بنویسم یا راهنماییم کنین چه کنم.

----------


## hamedjim

سلام به همگی و به ویژه به یوسف عزیز.
فکر می کنم قبلا جایی این موضوع رو دیده بودم ولی در سرچ چیزی پیدا نکردم.
در برنامه ای نیاز دارم تا از روش های محاسبات عددی برای حل معادلات غیر خطی استفاده کنم. از اونجایی که سالها از دوران دانشگاه گذشته، خیلی این روش ها رو به خاطر ندارم. به تابعی نیاز دارم که یک معادله رو دریافت و اون رو حل کنه. به عنوان مثال جواب معادله زیر رو می خوام بدونم:
(Sin(X/2))/X=C

مقدار C هم یک عدد حقیقی دلخواه هست.




ممنون میشم اگر می تونید من رو راهنمایی کنید.

----------


## یوسف زالی

سلام. این رو ببینید:
https://barnamenevis.org/showthread....=1#post1721046
ولی این برای توابع اکیدا یکنوا کار می کنه، یک محاسباتی هم بود فکر کنم به نام ریشه یابی نیوتنی، اون هم خوبه اما چون سینوس دوره تناوب داره ممکنه هیچ وقت همگرایی اتفاق نیفته.

----------


## hamedjim

> سلام. این رو ببینید:
> https://barnamenevis.org/showthread....=1#post1721046
> ولی این برای توابع اکیدا یکنوا کار می کنه، یک محاسباتی هم بود فکر کنم به نام ریشه یابی نیوتنی، اون هم خوبه اما چون سینوس دوره تناوب داره ممکنه هیچ وقت همگرایی اتفاق نیفته.


آره اون پست ریشه یابی رو دیدم. ولی چون برای برنامه من که به شدن به توابع مثلثاتی و قطبی وابسته س، خیلی برام کاربرد نداشت. روش نیوتون هم ظاهرا خیلی به انتخاب مقادیر اولیه وابسته س و کار من رو راه نمیندازه.
چقدر امیدوار بودم یه تابع شسته رفته و آماده برام ظاهر بشه :لبخند گشاده!: 
 فکر کنم بای یه دور دیگه بشینم محاسبات عددی پاس کنم تا بفهمم بهترین روش چیه :ناراحت:

----------

